(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  (function (global){
  /*
   * Copyright (c) 2012-2013 Chris Pettitt
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  graphlibDot = require("./index");
  graphlibDot.graphlib = require("graphlib");
  global.graphlibDot = graphlibDot;
  
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  },{"./index":2,"graphlib":9}],2:[function(require,module,exports){
  var read = require("./lib/read-one"),
      readMany = require("./lib/read-many"),
      write = require("./lib/write-one"),
      version = require("./lib/version");
  
  module.exports = {
    // Parsing
    read: read,
    readMany: readMany,
  
    // Writing
    write: write,
  
    // Version
    version: version,
  
    // For levelup encoding
    type: "dot",
    buffer: false
  };
  
  },{"./lib/read-many":5,"./lib/read-one":6,"./lib/version":7,"./lib/write-one":8}],3:[function(require,module,exports){
  "use strict";
  
  var _ = require("lodash"),
      Graph = require("graphlib").Graph;
  
  module.exports = buildGraph;
  
  function buildGraph(parseTree) {
    var isDirected = parseTree.type !== "graph",
        isMultigraph = !parseTree.strict,
        defaultStack = [{ node: {}, edge: {} }],
        g = new Graph({ directed: isDirected, multigraph: isMultigraph, compound: true });
        g.setGraph({});
    _.each(parseTree.stmts, function(stmt) { handleStmt(g, stmt, defaultStack); });
    return g;
  }
  
  function handleStmt(g, stmt, defaultStack, sg) {
    switch(stmt.type) {
      case "node": handleNodeStmt(g, stmt, defaultStack, sg); break;
      case "edge": handleEdgeStmt(g, stmt, defaultStack, sg); break;
      case "subgraph": handleSubgraphStmt(g, stmt, defaultStack, sg); break;
      case "attr": handleAttrStmt(g, stmt, defaultStack); break;
      case "inlineAttr": handleInlineAttrsStmt(g, stmt, defaultStack, sg); break;
    }
  }
  
  function handleNodeStmt(g, stmt, defaultStack, sg) {
    var v = stmt.id,
        attrs = stmt.attrs;
    maybeCreateNode(g, v, defaultStack, sg);
    _.merge(g.node(v), attrs);
  }
  
  function handleEdgeStmt(g, stmt, defaultStack, sg) {
    var attrs = stmt.attrs,
        prev, curr;
    _.each(stmt.elems, function(elem) {
      handleStmt(g, elem, defaultStack, sg);
  
      switch(elem.type) {
        case "node": curr = [elem.id]; break;
        case "subgraph": curr = collectNodeIds(elem); break;
      }
  
      _.each(prev, function(v) {
        _.each(curr, function(w) {
          var name;
          if (g.hasEdge(v, w) && g.isMultigraph()) {
            name = _.uniqueId("edge");
          }
          if (!g.hasEdge(v, w, name)) {
            g.setEdge(v, w, _.clone(_.last(defaultStack).edge), name);
          }
          _.merge(g.edge(v, w, name), attrs);
        });
      });
  
      prev = curr;
    });
  }
  
  function handleSubgraphStmt(g, stmt, defaultStack, sg) {
    var id = stmt.id;
    if (id === undefined) {
      id = generateSubgraphId(g);
    }
  
    defaultStack.push(_.clone(_.last(defaultStack)));
  
    maybeCreateNode(g, id, defaultStack, sg);
  
    _.each(stmt.stmts, function(s) {
      handleStmt(g, s, defaultStack, id);
    });
  
    // If there are no statements remove the subgraph
    if (!g.children(id).length) {
      g.removeNode(id);
    }
  
    defaultStack.pop();
  }
  
  function handleAttrStmt(g, stmt, defaultStack) {
    _.merge(_.last(defaultStack)[stmt.attrType], stmt.attrs);
  }
  
  function handleInlineAttrsStmt(g, stmt, defaultStack, sg) {
    _.merge(sg ? g.node(sg) : g.graph(), stmt.attrs);
  }
  
  function generateSubgraphId(g) {
    var id;
    do {
      id = _.uniqueId("sg");
    } while (g.hasNode(id));
    return id;
  }
  
  function maybeCreateNode(g, v, defaultStack, sg) {
    if (!g.hasNode(v)) {
      g.setNode(v, _.clone(_.last(defaultStack).node));
      g.setParent(v, sg);
    }
  }
  
  // Collect all nodes involved in a subgraph statement
  function collectNodeIds(stmt) {
    var ids = {},
        stack = [],
        curr;
  
    var push = stack.push.bind(stack);
  
    push(stmt);
    while(stack.length) {
      curr = stack.pop();
      switch(curr.type) {
        case "node": ids[curr.id] = true; break;
        case "edge": _.each(curr.elems, push); break;
        case "subgraph": _.each(curr.stmts, push); break;
      }
    }
  
    return _.keys(ids);
  }
  
  
  },{"graphlib":9,"lodash":28}],4:[function(require,module,exports){
  module.exports = (function() {
    /*
     * Generated by PEG.js 0.8.0.
     *
     * http://pegjs.majda.cz/
     */
  
    function peg$subclass(child, parent) {
      function ctor() { this.constructor = child; }
      ctor.prototype = parent.prototype;
      child.prototype = new ctor();
    }
  
    function SyntaxError(message, expected, found, offset, line, column) {
      this.message  = message;
      this.expected = expected;
      this.found    = found;
      this.offset   = offset;
      this.line     = line;
      this.column   = column;
  
      this.name     = "SyntaxError";
    }
  
    peg$subclass(SyntaxError, Error);
  
    function parse(input) {
      var options = arguments.length > 1 ? arguments[1] : {},
  
          peg$FAILED = {},
  
          peg$startRuleFunctions = { start: peg$parsestart, graphStmt: peg$parsegraphStmt },
          peg$startRuleFunction  = peg$parsestart,
  
          peg$c0 = [],
          peg$c1 = peg$FAILED,
          peg$c2 = null,
          peg$c3 = "{",
          peg$c4 = { type: "literal", value: "{", description: "\"{\"" },
          peg$c5 = "}",
          peg$c6 = { type: "literal", value: "}", description: "\"}\"" },
          peg$c7 = function(strict, type, id, stmts) {
                return {type: type, id: id, strict: strict !== null, stmts: stmts};
              },
          peg$c8 = ";",
          peg$c9 = { type: "literal", value: ";", description: "\";\"" },
          peg$c10 = function(first, rest) {
                var result = [first];
                for (var i = 0; i < rest.length; ++i) {
                  result.push(rest[i][1]);
                }
                return result;
              },
          peg$c11 = function(type, attrs) {
                return { type: "attr", attrType: type, attrs: attrs || {}};
              },
          peg$c12 = "=",
          peg$c13 = { type: "literal", value: "=", description: "\"=\"" },
          peg$c14 = function(k, v) {
                var attrs = {};
                attrs[k] = v;
                return { type: "inlineAttr", attrs: attrs };
              },
          peg$c15 = function(id, attrs) { return {type: "node", id: id, attrs: attrs || {}}; },
          peg$c16 = function(lhs, rhs, attrs) {
                var elems = [lhs];
                for (var i = 0; i < rhs.length; ++i) {
                  elems.push(rhs[i]);
                }
                return { type: "edge", elems: elems, attrs: attrs || {} };
              },
          peg$c17 = function(id, stmts) {
                id = (id && id[2]) || [];
                return { type: "subgraph", id: id[0], stmts: stmts };
              },
          peg$c18 = function(first, rest) {
                var result = first;
                for (var i = 0; i < rest.length; ++i) {
                  _.merge(result, rest[i][1]);
                }
                return result;
              },
          peg$c19 = "[",
          peg$c20 = { type: "literal", value: "[", description: "\"[\"" },
          peg$c21 = "]",
          peg$c22 = { type: "literal", value: "]", description: "\"]\"" },
          peg$c23 = function(aList) { return aList; },
          peg$c24 = ",",
          peg$c25 = { type: "literal", value: ",", description: "\",\"" },
          peg$c26 = function(first, rest) {
                var result = first;
                for (var i = 0; i < rest.length; ++i) {
                  _.merge(result, rest[i][3]);
                }
                return result;
              },
          peg$c27 = "--",
          peg$c28 = { type: "literal", value: "--", description: "\"--\"" },
          peg$c29 = function() { return directed; },
          peg$c30 = void 0,
          peg$c31 = "->",
          peg$c32 = { type: "literal", value: "->", description: "\"->\"" },
          peg$c33 = function(rhs, rest) {
                var result = [rhs];
                if (rest) {
                  for (var i = 0; i < rest.length; ++i) {
                    result.push(rest[i]);
                  }
                }
                return result;
              },
          peg$c34 = function(k, v) {
                var result = {};
                result[k] = v[3];
                return result;
              },
          peg$c35 = function(id) { return { type: "node", id: id, attrs: {} }; },
          peg$c36 = function(id) { return id; },
          peg$c37 = ":",
          peg$c38 = { type: "literal", value: ":", description: "\":\"" },
          peg$c39 = "ne",
          peg$c40 = { type: "literal", value: "ne", description: "\"ne\"" },
          peg$c41 = "se",
          peg$c42 = { type: "literal", value: "se", description: "\"se\"" },
          peg$c43 = "sw",
          peg$c44 = { type: "literal", value: "sw", description: "\"sw\"" },
          peg$c45 = "nw",
          peg$c46 = { type: "literal", value: "nw", description: "\"nw\"" },
          peg$c47 = "n",
          peg$c48 = { type: "literal", value: "n", description: "\"n\"" },
          peg$c49 = "e",
          peg$c50 = { type: "literal", value: "e", description: "\"e\"" },
          peg$c51 = "s",
          peg$c52 = { type: "literal", value: "s", description: "\"s\"" },
          peg$c53 = "w",
          peg$c54 = { type: "literal", value: "w", description: "\"w\"" },
          peg$c55 = "c",
          peg$c56 = { type: "literal", value: "c", description: "\"c\"" },
          peg$c57 = "_",
          peg$c58 = { type: "literal", value: "_", description: "\"_\"" },
          peg$c59 = { type: "other", description: "identifier" },
          peg$c60 = /^[a-zA-Z\u0200-\u0377_]/,
          peg$c61 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_]", description: "[a-zA-Z\\u0200-\\u0377_]" },
          peg$c62 = /^[a-zA-Z\u0200-\u0377_0-9]/,
          peg$c63 = { type: "class", value: "[a-zA-Z\\u0200-\\u0377_0-9]", description: "[a-zA-Z\\u0200-\\u0377_0-9]" },
          peg$c64 = function(fst, rest) { return fst + rest.join(""); },
          peg$c65 = "-",
          peg$c66 = { type: "literal", value: "-", description: "\"-\"" },
          peg$c67 = ".",
          peg$c68 = { type: "literal", value: ".", description: "\".\"" },
          peg$c69 = /^[0-9]/,
          peg$c70 = { type: "class", value: "[0-9]", description: "[0-9]" },
          peg$c71 = function(sign, dot, after) {
                return (sign || "") + dot + after.join("");
              },
          peg$c72 = function(sign, before, after) {
                return (sign || "") + before.join("") + (after ? after[0] : "") + (after ? after[1].join("") : "");
              },
          peg$c73 = "\"",
          peg$c74 = { type: "literal", value: "\"", description: "\"\\\"\"" },
          peg$c75 = "\\\"",
          peg$c76 = { type: "literal", value: "\\\"", description: "\"\\\\\\\"\"" },
          peg$c77 = function() { return '"'; },
          peg$c78 = "\\",
          peg$c79 = { type: "literal", value: "\\", description: "\"\\\\\"" },
          peg$c80 = /^[^"]/,
          peg$c81 = { type: "class", value: "[^\"]", description: "[^\"]" },
          peg$c82 = function(ch) { return "\\" + ch; },
          peg$c83 = function(id) {
                return id.join("");
              },
          peg$c84 = "node",
          peg$c85 = { type: "literal", value: "node", description: "\"node\"" },
          peg$c86 = function(k) { return k.toLowerCase(); },
          peg$c87 = "edge",
          peg$c88 = { type: "literal", value: "edge", description: "\"edge\"" },
          peg$c89 = "graph",
          peg$c90 = { type: "literal", value: "graph", description: "\"graph\"" },
          peg$c91 = "digraph",
          peg$c92 = { type: "literal", value: "digraph", description: "\"digraph\"" },
          peg$c93 = "subgraph",
          peg$c94 = { type: "literal", value: "subgraph", description: "\"subgraph\"" },
          peg$c95 = "strict",
          peg$c96 = { type: "literal", value: "strict", description: "\"strict\"" },
          peg$c97 = function(graph) {
                directed = graph === "digraph";
                return graph;
              },
          peg$c98 = { type: "other", description: "whitespace" },
          peg$c99 = /^[ \t\r\n]/,
          peg$c100 = { type: "class", value: "[ \\t\\r\\n]", description: "[ \\t\\r\\n]" },
          peg$c101 = { type: "other", description: "comment" },
          peg$c102 = "//",
          peg$c103 = { type: "literal", value: "//", description: "\"//\"" },
          peg$c104 = /^[^\n]/,
          peg$c105 = { type: "class", value: "[^\\n]", description: "[^\\n]" },
          peg$c106 = "/*",
          peg$c107 = { type: "literal", value: "/*", description: "\"/*\"" },
          peg$c108 = "*/",
          peg$c109 = { type: "literal", value: "*/", description: "\"*/\"" },
          peg$c110 = { type: "any", description: "any character" },
  
          peg$currPos          = 0,
          peg$reportedPos      = 0,
          peg$cachedPos        = 0,
          peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
          peg$maxFailPos       = 0,
          peg$maxFailExpected  = [],
          peg$silentFails      = 0,
  
          peg$result;
  
      if ("startRule" in options) {
        if (!(options.startRule in peg$startRuleFunctions)) {
          throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
        }
  
        peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
      }
  
      function text() {
        return input.substring(peg$reportedPos, peg$currPos);
      }
  
      function offset() {
        return peg$reportedPos;
      }
  
      function line() {
        return peg$computePosDetails(peg$reportedPos).line;
      }
  
      function column() {
        return peg$computePosDetails(peg$reportedPos).column;
      }
  
      function expected(description) {
        throw peg$buildException(
          null,
          [{ type: "other", description: description }],
          peg$reportedPos
        );
      }
  
      function error(message) {
        throw peg$buildException(message, null, peg$reportedPos);
      }
  
      function peg$computePosDetails(pos) {
        function advance(details, startPos, endPos) {
          var p, ch;
  
          for (p = startPos; p < endPos; p++) {
            ch = input.charAt(p);
            if (ch === "\n") {
              if (!details.seenCR) { details.line++; }
              details.column = 1;
              details.seenCR = false;
            } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
              details.line++;
              details.column = 1;
              details.seenCR = true;
            } else {
              details.column++;
              details.seenCR = false;
            }
          }
        }
  
        if (peg$cachedPos !== pos) {
          if (peg$cachedPos > pos) {
            peg$cachedPos = 0;
            peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
          }
          advance(peg$cachedPosDetails, peg$cachedPos, pos);
          peg$cachedPos = pos;
        }
  
        return peg$cachedPosDetails;
      }
  
      function peg$fail(expected) {
        if (peg$currPos < peg$maxFailPos) { return; }
  
        if (peg$currPos > peg$maxFailPos) {
          peg$maxFailPos = peg$currPos;
          peg$maxFailExpected = [];
        }
  
        peg$maxFailExpected.push(expected);
      }
  
      function peg$buildException(message, expected, pos) {
        function cleanupExpected(expected) {
          var i = 1;
  
          expected.sort(function(a, b) {
            if (a.description < b.description) {
              return -1;
            } else if (a.description > b.description) {
              return 1;
            } else {
              return 0;
            }
          });
  
          while (i < expected.length) {
            if (expected[i - 1] === expected[i]) {
              expected.splice(i, 1);
            } else {
              i++;
            }
          }
        }
  
        function buildMessage(expected, found) {
          function stringEscape(s) {
            function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
  
            return s
              .replace(/\\/g,   '\\\\')
              .replace(/"/g,    '\\"')
              .replace(/\x08/g, '\\b')
              .replace(/\t/g,   '\\t')
              .replace(/\n/g,   '\\n')
              .replace(/\f/g,   '\\f')
              .replace(/\r/g,   '\\r')
              .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
              .replace(/[\x10-\x1F\x80-\xFF]/g,    function(ch) { return '\\x'  + hex(ch); })
              .replace(/[\u0180-\u0FFF]/g,         function(ch) { return '\\u0' + hex(ch); })
              .replace(/[\u1080-\uFFFF]/g,         function(ch) { return '\\u'  + hex(ch); });
          }
  
          var expectedDescs = new Array(expected.length),
              expectedDesc, foundDesc, i;
  
          for (i = 0; i < expected.length; i++) {
            expectedDescs[i] = expected[i].description;
          }
  
          expectedDesc = expected.length > 1
            ? expectedDescs.slice(0, -1).join(", ")
                + " or "
                + expectedDescs[expected.length - 1]
            : expectedDescs[0];
  
          foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
  
          return "Expected " + expectedDesc + " but " + foundDesc + " found.";
        }
  
        var posDetails = peg$computePosDetails(pos),
            found      = pos < input.length ? input.charAt(pos) : null;
  
        if (expected !== null) {
          cleanupExpected(expected);
        }
  
        return new SyntaxError(
          message !== null ? message : buildMessage(expected, found),
          expected,
          found,
          pos,
          posDetails.line,
          posDetails.column
        );
      }
  
      function peg$parsestart() {
        var s0, s1;
  
        s0 = [];
        s1 = peg$parsegraphStmt();
        if (s1 !== peg$FAILED) {
          while (s1 !== peg$FAILED) {
            s0.push(s1);
            s1 = peg$parsegraphStmt();
          }
        } else {
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsegraphStmt() {
        var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
  
        s0 = peg$currPos;
        s1 = [];
        s2 = peg$parse_();
        while (s2 !== peg$FAILED) {
          s1.push(s2);
          s2 = peg$parse_();
        }
        if (s1 !== peg$FAILED) {
          s2 = peg$currPos;
          s3 = peg$parsestrict();
          if (s3 !== peg$FAILED) {
            s4 = peg$parse_();
            if (s4 !== peg$FAILED) {
              s3 = [s3, s4];
              s2 = s3;
            } else {
              peg$currPos = s2;
              s2 = peg$c1;
            }
          } else {
            peg$currPos = s2;
            s2 = peg$c1;
          }
          if (s2 === peg$FAILED) {
            s2 = peg$c2;
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parsegraphType();
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                s5 = peg$parseid();
                if (s5 === peg$FAILED) {
                  s5 = peg$c2;
                }
                if (s5 !== peg$FAILED) {
                  s6 = [];
                  s7 = peg$parse_();
                  while (s7 !== peg$FAILED) {
                    s6.push(s7);
                    s7 = peg$parse_();
                  }
                  if (s6 !== peg$FAILED) {
                    if (input.charCodeAt(peg$currPos) === 123) {
                      s7 = peg$c3;
                      peg$currPos++;
                    } else {
                      s7 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c4); }
                    }
                    if (s7 !== peg$FAILED) {
                      s8 = [];
                      s9 = peg$parse_();
                      while (s9 !== peg$FAILED) {
                        s8.push(s9);
                        s9 = peg$parse_();
                      }
                      if (s8 !== peg$FAILED) {
                        s9 = peg$parsestmtList();
                        if (s9 === peg$FAILED) {
                          s9 = peg$c2;
                        }
                        if (s9 !== peg$FAILED) {
                          s10 = [];
                          s11 = peg$parse_();
                          while (s11 !== peg$FAILED) {
                            s10.push(s11);
                            s11 = peg$parse_();
                          }
                          if (s10 !== peg$FAILED) {
                            if (input.charCodeAt(peg$currPos) === 125) {
                              s11 = peg$c5;
                              peg$currPos++;
                            } else {
                              s11 = peg$FAILED;
                              if (peg$silentFails === 0) { peg$fail(peg$c6); }
                            }
                            if (s11 !== peg$FAILED) {
                              s12 = [];
                              s13 = peg$parse_();
                              while (s13 !== peg$FAILED) {
                                s12.push(s13);
                                s13 = peg$parse_();
                              }
                              if (s12 !== peg$FAILED) {
                                peg$reportedPos = s0;
                                s1 = peg$c7(s2, s3, s5, s9);
                                s0 = s1;
                              } else {
                                peg$currPos = s0;
                                s0 = peg$c1;
                              }
                            } else {
                              peg$currPos = s0;
                              s0 = peg$c1;
                            }
                          } else {
                            peg$currPos = s0;
                            s0 = peg$c1;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$c1;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$c1;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$c1;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsestmtList() {
        var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
  
        s0 = peg$currPos;
        s1 = peg$parsestmt();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 59) {
              s3 = peg$c8;
              peg$currPos++;
            } else {
              s3 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c9); }
            }
            if (s3 === peg$FAILED) {
              s3 = peg$c2;
            }
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$currPos;
              s6 = [];
              s7 = peg$parse_();
              while (s7 !== peg$FAILED) {
                s6.push(s7);
                s7 = peg$parse_();
              }
              if (s6 !== peg$FAILED) {
                s7 = peg$parsestmt();
                if (s7 !== peg$FAILED) {
                  s8 = [];
                  s9 = peg$parse_();
                  while (s9 !== peg$FAILED) {
                    s8.push(s9);
                    s9 = peg$parse_();
                  }
                  if (s8 !== peg$FAILED) {
                    if (input.charCodeAt(peg$currPos) === 59) {
                      s9 = peg$c8;
                      peg$currPos++;
                    } else {
                      s9 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c9); }
                    }
                    if (s9 === peg$FAILED) {
                      s9 = peg$c2;
                    }
                    if (s9 !== peg$FAILED) {
                      s6 = [s6, s7, s8, s9];
                      s5 = s6;
                    } else {
                      peg$currPos = s5;
                      s5 = peg$c1;
                    }
                  } else {
                    peg$currPos = s5;
                    s5 = peg$c1;
                  }
                } else {
                  peg$currPos = s5;
                  s5 = peg$c1;
                }
              } else {
                peg$currPos = s5;
                s5 = peg$c1;
              }
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$currPos;
                s6 = [];
                s7 = peg$parse_();
                while (s7 !== peg$FAILED) {
                  s6.push(s7);
                  s7 = peg$parse_();
                }
                if (s6 !== peg$FAILED) {
                  s7 = peg$parsestmt();
                  if (s7 !== peg$FAILED) {
                    s8 = [];
                    s9 = peg$parse_();
                    while (s9 !== peg$FAILED) {
                      s8.push(s9);
                      s9 = peg$parse_();
                    }
                    if (s8 !== peg$FAILED) {
                      if (input.charCodeAt(peg$currPos) === 59) {
                        s9 = peg$c8;
                        peg$currPos++;
                      } else {
                        s9 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c9); }
                      }
                      if (s9 === peg$FAILED) {
                        s9 = peg$c2;
                      }
                      if (s9 !== peg$FAILED) {
                        s6 = [s6, s7, s8, s9];
                        s5 = s6;
                      } else {
                        peg$currPos = s5;
                        s5 = peg$c1;
                      }
                    } else {
                      peg$currPos = s5;
                      s5 = peg$c1;
                    }
                  } else {
                    peg$currPos = s5;
                    s5 = peg$c1;
                  }
                } else {
                  peg$currPos = s5;
                  s5 = peg$c1;
                }
              }
              if (s4 !== peg$FAILED) {
                peg$reportedPos = s0;
                s1 = peg$c10(s1, s4);
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsestmt() {
        var s0;
  
        s0 = peg$parseattrStmt();
        if (s0 === peg$FAILED) {
          s0 = peg$parseedgeStmt();
          if (s0 === peg$FAILED) {
            s0 = peg$parsesubgraphStmt();
            if (s0 === peg$FAILED) {
              s0 = peg$parseinlineAttrStmt();
              if (s0 === peg$FAILED) {
                s0 = peg$parsenodeStmt();
              }
            }
          }
        }
  
        return s0;
      }
  
      function peg$parseattrStmt() {
        var s0, s1, s2, s3;
  
        s0 = peg$currPos;
        s1 = peg$parsegraph();
        if (s1 === peg$FAILED) {
          s1 = peg$parsenode();
          if (s1 === peg$FAILED) {
            s1 = peg$parseedge();
          }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseattrList();
            if (s3 !== peg$FAILED) {
              peg$reportedPos = s0;
              s1 = peg$c11(s1, s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseinlineAttrStmt() {
        var s0, s1, s2, s3, s4, s5;
  
        s0 = peg$currPos;
        s1 = peg$parseid();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 61) {
              s3 = peg$c12;
              peg$currPos++;
            } else {
              s3 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c13); }
            }
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                s5 = peg$parseid();
                if (s5 !== peg$FAILED) {
                  peg$reportedPos = s0;
                  s1 = peg$c14(s1, s5);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsenodeStmt() {
        var s0, s1, s2, s3;
  
        s0 = peg$currPos;
        s1 = peg$parsenodeId();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseattrList();
            if (s3 === peg$FAILED) {
              s3 = peg$c2;
            }
            if (s3 !== peg$FAILED) {
              peg$reportedPos = s0;
              s1 = peg$c15(s1, s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseedgeStmt() {
        var s0, s1, s2, s3, s4, s5;
  
        s0 = peg$currPos;
        s1 = peg$parsenodeIdOrSubgraph();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseedgeRHS();
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                s5 = peg$parseattrList();
                if (s5 === peg$FAILED) {
                  s5 = peg$c2;
                }
                if (s5 !== peg$FAILED) {
                  peg$reportedPos = s0;
                  s1 = peg$c16(s1, s3, s5);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsesubgraphStmt() {
        var s0, s1, s2, s3, s4, s5, s6, s7;
  
        s0 = peg$currPos;
        s1 = peg$currPos;
        s2 = peg$parsesubgraph();
        if (s2 !== peg$FAILED) {
          s3 = [];
          s4 = peg$parse_();
          while (s4 !== peg$FAILED) {
            s3.push(s4);
            s4 = peg$parse_();
          }
          if (s3 !== peg$FAILED) {
            s4 = peg$currPos;
            s5 = peg$parseid();
            if (s5 !== peg$FAILED) {
              s6 = [];
              s7 = peg$parse_();
              while (s7 !== peg$FAILED) {
                s6.push(s7);
                s7 = peg$parse_();
              }
              if (s6 !== peg$FAILED) {
                s5 = [s5, s6];
                s4 = s5;
              } else {
                peg$currPos = s4;
                s4 = peg$c1;
              }
            } else {
              peg$currPos = s4;
              s4 = peg$c1;
            }
            if (s4 === peg$FAILED) {
              s4 = peg$c2;
            }
            if (s4 !== peg$FAILED) {
              s2 = [s2, s3, s4];
              s1 = s2;
            } else {
              peg$currPos = s1;
              s1 = peg$c1;
            }
          } else {
            peg$currPos = s1;
            s1 = peg$c1;
          }
        } else {
          peg$currPos = s1;
          s1 = peg$c1;
        }
        if (s1 === peg$FAILED) {
          s1 = peg$c2;
        }
        if (s1 !== peg$FAILED) {
          if (input.charCodeAt(peg$currPos) === 123) {
            s2 = peg$c3;
            peg$currPos++;
          } else {
            s2 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c4); }
          }
          if (s2 !== peg$FAILED) {
            s3 = [];
            s4 = peg$parse_();
            while (s4 !== peg$FAILED) {
              s3.push(s4);
              s4 = peg$parse_();
            }
            if (s3 !== peg$FAILED) {
              s4 = peg$parsestmtList();
              if (s4 === peg$FAILED) {
                s4 = peg$c2;
              }
              if (s4 !== peg$FAILED) {
                s5 = [];
                s6 = peg$parse_();
                while (s6 !== peg$FAILED) {
                  s5.push(s6);
                  s6 = peg$parse_();
                }
                if (s5 !== peg$FAILED) {
                  if (input.charCodeAt(peg$currPos) === 125) {
                    s6 = peg$c5;
                    peg$currPos++;
                  } else {
                    s6 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c6); }
                  }
                  if (s6 !== peg$FAILED) {
                    peg$reportedPos = s0;
                    s1 = peg$c17(s1, s4);
                    s0 = s1;
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseattrList() {
        var s0, s1, s2, s3, s4, s5;
  
        s0 = peg$currPos;
        s1 = peg$parseattrListBlock();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$currPos;
          s4 = [];
          s5 = peg$parse_();
          while (s5 !== peg$FAILED) {
            s4.push(s5);
            s5 = peg$parse_();
          }
          if (s4 !== peg$FAILED) {
            s5 = peg$parseattrListBlock();
            if (s5 !== peg$FAILED) {
              s4 = [s4, s5];
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$currPos;
            s4 = [];
            s5 = peg$parse_();
            while (s5 !== peg$FAILED) {
              s4.push(s5);
              s5 = peg$parse_();
            }
            if (s4 !== peg$FAILED) {
              s5 = peg$parseattrListBlock();
              if (s5 !== peg$FAILED) {
                s4 = [s4, s5];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          }
          if (s2 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c18(s1, s2);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseattrListBlock() {
        var s0, s1, s2, s3, s4, s5;
  
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 91) {
          s1 = peg$c19;
          peg$currPos++;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c20); }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseaList();
            if (s3 === peg$FAILED) {
              s3 = peg$c2;
            }
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                if (input.charCodeAt(peg$currPos) === 93) {
                  s5 = peg$c21;
                  peg$currPos++;
                } else {
                  s5 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c22); }
                }
                if (s5 !== peg$FAILED) {
                  peg$reportedPos = s0;
                  s1 = peg$c23(s3);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseaList() {
        var s0, s1, s2, s3, s4, s5, s6, s7;
  
        s0 = peg$currPos;
        s1 = peg$parseidDef();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$currPos;
          s4 = [];
          s5 = peg$parse_();
          while (s5 !== peg$FAILED) {
            s4.push(s5);
            s5 = peg$parse_();
          }
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 44) {
              s5 = peg$c24;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c25); }
            }
            if (s5 === peg$FAILED) {
              s5 = peg$c2;
            }
            if (s5 !== peg$FAILED) {
              s6 = [];
              s7 = peg$parse_();
              while (s7 !== peg$FAILED) {
                s6.push(s7);
                s7 = peg$parse_();
              }
              if (s6 !== peg$FAILED) {
                s7 = peg$parseidDef();
                if (s7 !== peg$FAILED) {
                  s4 = [s4, s5, s6, s7];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$c1;
          }
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$currPos;
            s4 = [];
            s5 = peg$parse_();
            while (s5 !== peg$FAILED) {
              s4.push(s5);
              s5 = peg$parse_();
            }
            if (s4 !== peg$FAILED) {
              if (input.charCodeAt(peg$currPos) === 44) {
                s5 = peg$c24;
                peg$currPos++;
              } else {
                s5 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c25); }
              }
              if (s5 === peg$FAILED) {
                s5 = peg$c2;
              }
              if (s5 !== peg$FAILED) {
                s6 = [];
                s7 = peg$parse_();
                while (s7 !== peg$FAILED) {
                  s6.push(s7);
                  s7 = peg$parse_();
                }
                if (s6 !== peg$FAILED) {
                  s7 = peg$parseidDef();
                  if (s7 !== peg$FAILED) {
                    s4 = [s4, s5, s6, s7];
                    s3 = s4;
                  } else {
                    peg$currPos = s3;
                    s3 = peg$c1;
                  }
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
          }
          if (s2 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c26(s1, s2);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseedgeRHS() {
        var s0, s1, s2, s3, s4, s5;
  
        s0 = peg$currPos;
        s1 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c27) {
          s2 = peg$c27;
          peg$currPos += 2;
        } else {
          s2 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c28); }
        }
        if (s2 !== peg$FAILED) {
          peg$reportedPos = peg$currPos;
          s3 = peg$c29();
          if (s3) {
            s3 = peg$c1;
          } else {
            s3 = peg$c30;
          }
          if (s3 !== peg$FAILED) {
            s2 = [s2, s3];
            s1 = s2;
          } else {
            peg$currPos = s1;
            s1 = peg$c1;
          }
        } else {
          peg$currPos = s1;
          s1 = peg$c1;
        }
        if (s1 === peg$FAILED) {
          s1 = peg$currPos;
          if (input.substr(peg$currPos, 2) === peg$c31) {
            s2 = peg$c31;
            peg$currPos += 2;
          } else {
            s2 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c32); }
          }
          if (s2 !== peg$FAILED) {
            peg$reportedPos = peg$currPos;
            s3 = peg$c29();
            if (s3) {
              s3 = peg$c30;
            } else {
              s3 = peg$c1;
            }
            if (s3 !== peg$FAILED) {
              s2 = [s2, s3];
              s1 = s2;
            } else {
              peg$currPos = s1;
              s1 = peg$c1;
            }
          } else {
            peg$currPos = s1;
            s1 = peg$c1;
          }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parsenodeIdOrSubgraph();
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                s5 = peg$parseedgeRHS();
                if (s5 === peg$FAILED) {
                  s5 = peg$c2;
                }
                if (s5 !== peg$FAILED) {
                  peg$reportedPos = s0;
                  s1 = peg$c33(s3, s5);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseidDef() {
        var s0, s1, s2, s3, s4, s5, s6;
  
        s0 = peg$currPos;
        s1 = peg$parseid();
        if (s1 !== peg$FAILED) {
          s2 = peg$currPos;
          s3 = [];
          s4 = peg$parse_();
          while (s4 !== peg$FAILED) {
            s3.push(s4);
            s4 = peg$parse_();
          }
          if (s3 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 61) {
              s4 = peg$c12;
              peg$currPos++;
            } else {
              s4 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c13); }
            }
            if (s4 !== peg$FAILED) {
              s5 = [];
              s6 = peg$parse_();
              while (s6 !== peg$FAILED) {
                s5.push(s6);
                s6 = peg$parse_();
              }
              if (s5 !== peg$FAILED) {
                s6 = peg$parseid();
                if (s6 !== peg$FAILED) {
                  s3 = [s3, s4, s5, s6];
                  s2 = s3;
                } else {
                  peg$currPos = s2;
                  s2 = peg$c1;
                }
              } else {
                peg$currPos = s2;
                s2 = peg$c1;
              }
            } else {
              peg$currPos = s2;
              s2 = peg$c1;
            }
          } else {
            peg$currPos = s2;
            s2 = peg$c1;
          }
          if (s2 === peg$FAILED) {
            s2 = peg$c2;
          }
          if (s2 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c34(s1, s2);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsenodeIdOrSubgraph() {
        var s0, s1;
  
        s0 = peg$parsesubgraphStmt();
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          s1 = peg$parsenodeId();
          if (s1 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c35(s1);
          }
          s0 = s1;
        }
  
        return s0;
      }
  
      function peg$parsenodeId() {
        var s0, s1, s2, s3;
  
        s0 = peg$currPos;
        s1 = peg$parseid();
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseport();
            if (s3 === peg$FAILED) {
              s3 = peg$c2;
            }
            if (s3 !== peg$FAILED) {
              peg$reportedPos = s0;
              s1 = peg$c36(s1);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parseport() {
        var s0, s1, s2, s3, s4, s5, s6, s7, s8;
  
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 58) {
          s1 = peg$c37;
          peg$currPos++;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c38); }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          s3 = peg$parse_();
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            s3 = peg$parse_();
          }
          if (s2 !== peg$FAILED) {
            s3 = peg$parseid();
            if (s3 !== peg$FAILED) {
              s4 = [];
              s5 = peg$parse_();
              while (s5 !== peg$FAILED) {
                s4.push(s5);
                s5 = peg$parse_();
              }
              if (s4 !== peg$FAILED) {
                s5 = peg$currPos;
                if (input.charCodeAt(peg$currPos) === 58) {
                  s6 = peg$c37;
                  peg$currPos++;
                } else {
                  s6 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c38); }
                }
                if (s6 !== peg$FAILED) {
                  s7 = [];
                  s8 = peg$parse_();
                  while (s8 !== peg$FAILED) {
                    s7.push(s8);
                    s8 = peg$parse_();
                  }
                  if (s7 !== peg$FAILED) {
                    s8 = peg$parsecompassPt();
                    if (s8 !== peg$FAILED) {
                      s6 = [s6, s7, s8];
                      s5 = s6;
                    } else {
                      peg$currPos = s5;
                      s5 = peg$c1;
                    }
                  } else {
                    peg$currPos = s5;
                    s5 = peg$c1;
                  }
                } else {
                  peg$currPos = s5;
                  s5 = peg$c1;
                }
                if (s5 === peg$FAILED) {
                  s5 = peg$c2;
                }
                if (s5 !== peg$FAILED) {
                  s1 = [s1, s2, s3, s4, s5];
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
  
        return s0;
      }
  
      function peg$parsecompassPt() {
        var s0;
  
        if (input.substr(peg$currPos, 2) === peg$c39) {
          s0 = peg$c39;
          peg$currPos += 2;
        } else {
          s0 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c40); }
        }
        if (s0 === peg$FAILED) {
          if (input.substr(peg$currPos, 2) === peg$c41) {
            s0 = peg$c41;
            peg$currPos += 2;
          } else {
            s0 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c42); }
          }
          if (s0 === peg$FAILED) {
            if (input.substr(peg$currPos, 2) === peg$c43) {
              s0 = peg$c43;
              peg$currPos += 2;
            } else {
              s0 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c44); }
            }
            if (s0 === peg$FAILED) {
              if (input.substr(peg$currPos, 2) === peg$c45) {
                s0 = peg$c45;
                peg$currPos += 2;
              } else {
                s0 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c46); }
              }
              if (s0 === peg$FAILED) {
                if (input.charCodeAt(peg$currPos) === 110) {
                  s0 = peg$c47;
                  peg$currPos++;
                } else {
                  s0 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c48); }
                }
                if (s0 === peg$FAILED) {
                  if (input.charCodeAt(peg$currPos) === 101) {
                    s0 = peg$c49;
                    peg$currPos++;
                  } else {
                    s0 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c50); }
                  }
                  if (s0 === peg$FAILED) {
                    if (input.charCodeAt(peg$currPos) === 115) {
                      s0 = peg$c51;
                      peg$currPos++;
                    } else {
                      s0 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c52); }
                    }
                    if (s0 === peg$FAILED) {
                      if (input.charCodeAt(peg$currPos) === 119) {
                        s0 = peg$c53;
                        peg$currPos++;
                      } else {
                        s0 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c54); }
                      }
                      if (s0 === peg$FAILED) {
                        if (input.charCodeAt(peg$currPos) === 99) {
                          s0 = peg$c55;
                          peg$currPos++;
                        } else {
                          s0 = peg$FAILED;
                          if (peg$silentFails === 0) { peg$fail(peg$c56); }
                        }
                        if (s0 === peg$FAILED) {
                          if (input.charCodeAt(peg$currPos) === 95) {
                            s0 = peg$c57;
                            peg$currPos++;
                          } else {
                            s0 = peg$FAILED;
                            if (peg$silentFails === 0) { peg$fail(peg$c58); }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
  
        return s0;
      }
  
      function peg$parseid() {
        var s0, s1, s2, s3, s4, s5, s6;
  
        peg$silentFails++;
        s0 = peg$currPos;
        if (peg$c60.test(input.charAt(peg$currPos))) {
          s1 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c61); }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          if (peg$c62.test(input.charAt(peg$currPos))) {
            s3 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s3 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c63); }
          }
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            if (peg$c62.test(input.charAt(peg$currPos))) {
              s3 = input.charAt(peg$currPos);
              peg$currPos++;
            } else {
              s3 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c63); }
            }
          }
          if (s2 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c64(s1, s2);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 45) {
            s1 = peg$c65;
            peg$currPos++;
          } else {
            s1 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c66); }
          }
          if (s1 === peg$FAILED) {
            s1 = peg$c2;
          }
          if (s1 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 46) {
              s2 = peg$c67;
              peg$currPos++;
            } else {
              s2 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c68); }
            }
            if (s2 !== peg$FAILED) {
              s3 = [];
              if (peg$c69.test(input.charAt(peg$currPos))) {
                s4 = input.charAt(peg$currPos);
                peg$currPos++;
              } else {
                s4 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c70); }
              }
              if (s4 !== peg$FAILED) {
                while (s4 !== peg$FAILED) {
                  s3.push(s4);
                  if (peg$c69.test(input.charAt(peg$currPos))) {
                    s4 = input.charAt(peg$currPos);
                    peg$currPos++;
                  } else {
                    s4 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c70); }
                  }
                }
              } else {
                s3 = peg$c1;
              }
              if (s3 !== peg$FAILED) {
                peg$reportedPos = s0;
                s1 = peg$c71(s1, s2, s3);
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
          if (s0 === peg$FAILED) {
            s0 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 45) {
              s1 = peg$c65;
              peg$currPos++;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c66); }
            }
            if (s1 === peg$FAILED) {
              s1 = peg$c2;
            }
            if (s1 !== peg$FAILED) {
              s2 = [];
              if (peg$c69.test(input.charAt(peg$currPos))) {
                s3 = input.charAt(peg$currPos);
                peg$currPos++;
              } else {
                s3 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c70); }
              }
              if (s3 !== peg$FAILED) {
                while (s3 !== peg$FAILED) {
                  s2.push(s3);
                  if (peg$c69.test(input.charAt(peg$currPos))) {
                    s3 = input.charAt(peg$currPos);
                    peg$currPos++;
                  } else {
                    s3 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c70); }
                  }
                }
              } else {
                s2 = peg$c1;
              }
              if (s2 !== peg$FAILED) {
                s3 = peg$currPos;
                if (input.charCodeAt(peg$currPos) === 46) {
                  s4 = peg$c67;
                  peg$currPos++;
                } else {
                  s4 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c68); }
                }
                if (s4 !== peg$FAILED) {
                  s5 = [];
                  if (peg$c69.test(input.charAt(peg$currPos))) {
                    s6 = input.charAt(peg$currPos);
                    peg$currPos++;
                  } else {
                    s6 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c70); }
                  }
                  while (s6 !== peg$FAILED) {
                    s5.push(s6);
                    if (peg$c69.test(input.charAt(peg$currPos))) {
                      s6 = input.charAt(peg$currPos);
                      peg$currPos++;
                    } else {
                      s6 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c70); }
                    }
                  }
                  if (s5 !== peg$FAILED) {
                    s4 = [s4, s5];
                    s3 = s4;
                  } else {
                    peg$currPos = s3;
                    s3 = peg$c1;
                  }
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
                if (s3 === peg$FAILED) {
                  s3 = peg$c2;
                }
                if (s3 !== peg$FAILED) {
                  peg$reportedPos = s0;
                  s1 = peg$c72(s1, s2, s3);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
            if (s0 === peg$FAILED) {
              s0 = peg$currPos;
              if (input.charCodeAt(peg$currPos) === 34) {
                s1 = peg$c73;
                peg$currPos++;
              } else {
                s1 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c74); }
              }
              if (s1 !== peg$FAILED) {
                s2 = [];
                s3 = peg$currPos;
                if (input.substr(peg$currPos, 2) === peg$c75) {
                  s4 = peg$c75;
                  peg$currPos += 2;
                } else {
                  s4 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c76); }
                }
                if (s4 !== peg$FAILED) {
                  peg$reportedPos = s3;
                  s4 = peg$c77();
                }
                s3 = s4;
                if (s3 === peg$FAILED) {
                  s3 = peg$currPos;
                  if (input.charCodeAt(peg$currPos) === 92) {
                    s4 = peg$c78;
                    peg$currPos++;
                  } else {
                    s4 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c79); }
                  }
                  if (s4 !== peg$FAILED) {
                    if (peg$c80.test(input.charAt(peg$currPos))) {
                      s5 = input.charAt(peg$currPos);
                      peg$currPos++;
                    } else {
                      s5 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c81); }
                    }
                    if (s5 !== peg$FAILED) {
                      peg$reportedPos = s3;
                      s4 = peg$c82(s5);
                      s3 = s4;
                    } else {
                      peg$currPos = s3;
                      s3 = peg$c1;
                    }
                  } else {
                    peg$currPos = s3;
                    s3 = peg$c1;
                  }
                  if (s3 === peg$FAILED) {
                    if (peg$c80.test(input.charAt(peg$currPos))) {
                      s3 = input.charAt(peg$currPos);
                      peg$currPos++;
                    } else {
                      s3 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c81); }
                    }
                  }
                }
                while (s3 !== peg$FAILED) {
                  s2.push(s3);
                  s3 = peg$currPos;
                  if (input.substr(peg$currPos, 2) === peg$c75) {
                    s4 = peg$c75;
                    peg$currPos += 2;
                  } else {
                    s4 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c76); }
                  }
                  if (s4 !== peg$FAILED) {
                    peg$reportedPos = s3;
                    s4 = peg$c77();
                  }
                  s3 = s4;
                  if (s3 === peg$FAILED) {
                    s3 = peg$currPos;
                    if (input.charCodeAt(peg$currPos) === 92) {
                      s4 = peg$c78;
                      peg$currPos++;
                    } else {
                      s4 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c79); }
                    }
                    if (s4 !== peg$FAILED) {
                      if (peg$c80.test(input.charAt(peg$currPos))) {
                        s5 = input.charAt(peg$currPos);
                        peg$currPos++;
                      } else {
                        s5 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c81); }
                      }
                      if (s5 !== peg$FAILED) {
                        peg$reportedPos = s3;
                        s4 = peg$c82(s5);
                        s3 = s4;
                      } else {
                        peg$currPos = s3;
                        s3 = peg$c1;
                      }
                    } else {
                      peg$currPos = s3;
                      s3 = peg$c1;
                    }
                    if (s3 === peg$FAILED) {
                      if (peg$c80.test(input.charAt(peg$currPos))) {
                        s3 = input.charAt(peg$currPos);
                        peg$currPos++;
                      } else {
                        s3 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c81); }
                      }
                    }
                  }
                }
                if (s2 !== peg$FAILED) {
                  if (input.charCodeAt(peg$currPos) === 34) {
                    s3 = peg$c73;
                    peg$currPos++;
                  } else {
                    s3 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c74); }
                  }
                  if (s3 !== peg$FAILED) {
                    peg$reportedPos = s0;
                    s1 = peg$c83(s2);
                    s0 = s1;
                  } else {
                    peg$currPos = s0;
                    s0 = peg$c1;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$c1;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            }
          }
        }
        peg$silentFails--;
        if (s0 === peg$FAILED) {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c59); }
        }
  
        return s0;
      }
  
      function peg$parsenode() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 4).toLowerCase() === peg$c84) {
          s1 = input.substr(peg$currPos, 4);
          peg$currPos += 4;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c85); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parseedge() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 4).toLowerCase() === peg$c87) {
          s1 = input.substr(peg$currPos, 4);
          peg$currPos += 4;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c88); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parsegraph() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 5).toLowerCase() === peg$c89) {
          s1 = input.substr(peg$currPos, 5);
          peg$currPos += 5;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c90); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parsedigraph() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 7).toLowerCase() === peg$c91) {
          s1 = input.substr(peg$currPos, 7);
          peg$currPos += 7;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c92); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parsesubgraph() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 8).toLowerCase() === peg$c93) {
          s1 = input.substr(peg$currPos, 8);
          peg$currPos += 8;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c94); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parsestrict() {
        var s0, s1;
  
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 6).toLowerCase() === peg$c95) {
          s1 = input.substr(peg$currPos, 6);
          peg$currPos += 6;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c96); }
        }
        if (s1 !== peg$FAILED) {
          peg$reportedPos = s0;
          s1 = peg$c86(s1);
        }
        s0 = s1;
  
        return s0;
      }
  
      function peg$parsegraphType() {
        var s0, s1;
  
        s0 = peg$parsegraph();
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          s1 = peg$parsedigraph();
          if (s1 !== peg$FAILED) {
            peg$reportedPos = s0;
            s1 = peg$c97(s1);
          }
          s0 = s1;
        }
  
        return s0;
      }
  
      function peg$parsewhitespace() {
        var s0, s1;
  
        peg$silentFails++;
        s0 = [];
        if (peg$c99.test(input.charAt(peg$currPos))) {
          s1 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c100); }
        }
        if (s1 !== peg$FAILED) {
          while (s1 !== peg$FAILED) {
            s0.push(s1);
            if (peg$c99.test(input.charAt(peg$currPos))) {
              s1 = input.charAt(peg$currPos);
              peg$currPos++;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c100); }
            }
          }
        } else {
          s0 = peg$c1;
        }
        peg$silentFails--;
        if (s0 === peg$FAILED) {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c98); }
        }
  
        return s0;
      }
  
      function peg$parsecomment() {
        var s0, s1, s2, s3, s4, s5;
  
        peg$silentFails++;
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c102) {
          s1 = peg$c102;
          peg$currPos += 2;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c103); }
        }
        if (s1 !== peg$FAILED) {
          s2 = [];
          if (peg$c104.test(input.charAt(peg$currPos))) {
            s3 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s3 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c105); }
          }
          while (s3 !== peg$FAILED) {
            s2.push(s3);
            if (peg$c104.test(input.charAt(peg$currPos))) {
              s3 = input.charAt(peg$currPos);
              peg$currPos++;
            } else {
              s3 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c105); }
            }
          }
          if (s2 !== peg$FAILED) {
            s1 = [s1, s2];
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$c1;
        }
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          if (input.substr(peg$currPos, 2) === peg$c106) {
            s1 = peg$c106;
            peg$currPos += 2;
          } else {
            s1 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c107); }
          }
          if (s1 !== peg$FAILED) {
            s2 = [];
            s3 = peg$currPos;
            s4 = peg$currPos;
            peg$silentFails++;
            if (input.substr(peg$currPos, 2) === peg$c108) {
              s5 = peg$c108;
              peg$currPos += 2;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c109); }
            }
            peg$silentFails--;
            if (s5 === peg$FAILED) {
              s4 = peg$c30;
            } else {
              peg$currPos = s4;
              s4 = peg$c1;
            }
            if (s4 !== peg$FAILED) {
              if (input.length > peg$currPos) {
                s5 = input.charAt(peg$currPos);
                peg$currPos++;
              } else {
                s5 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c110); }
              }
              if (s5 !== peg$FAILED) {
                s4 = [s4, s5];
                s3 = s4;
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            } else {
              peg$currPos = s3;
              s3 = peg$c1;
            }
            while (s3 !== peg$FAILED) {
              s2.push(s3);
              s3 = peg$currPos;
              s4 = peg$currPos;
              peg$silentFails++;
              if (input.substr(peg$currPos, 2) === peg$c108) {
                s5 = peg$c108;
                peg$currPos += 2;
              } else {
                s5 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c109); }
              }
              peg$silentFails--;
              if (s5 === peg$FAILED) {
                s4 = peg$c30;
              } else {
                peg$currPos = s4;
                s4 = peg$c1;
              }
              if (s4 !== peg$FAILED) {
                if (input.length > peg$currPos) {
                  s5 = input.charAt(peg$currPos);
                  peg$currPos++;
                } else {
                  s5 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c110); }
                }
                if (s5 !== peg$FAILED) {
                  s4 = [s4, s5];
                  s3 = s4;
                } else {
                  peg$currPos = s3;
                  s3 = peg$c1;
                }
              } else {
                peg$currPos = s3;
                s3 = peg$c1;
              }
            }
            if (s2 !== peg$FAILED) {
              if (input.substr(peg$currPos, 2) === peg$c108) {
                s3 = peg$c108;
                peg$currPos += 2;
              } else {
                s3 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c109); }
              }
              if (s3 !== peg$FAILED) {
                s1 = [s1, s2, s3];
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$c1;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$c1;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$c1;
          }
        }
        peg$silentFails--;
        if (s0 === peg$FAILED) {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c101); }
        }
  
        return s0;
      }
  
      function peg$parse_() {
        var s0;
  
        s0 = peg$parsewhitespace();
        if (s0 === peg$FAILED) {
          s0 = peg$parsecomment();
        }
  
        return s0;
      }
  
  
        var _ = require("lodash");
        var directed;
  
  
      peg$result = peg$startRuleFunction();
  
      if (peg$result !== peg$FAILED && peg$currPos === input.length) {
        return peg$result;
      } else {
        if (peg$result !== peg$FAILED && peg$currPos < input.length) {
          peg$fail({ type: "end", description: "end of input" });
        }
  
        throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);
      }
    }
  
    return {
      SyntaxError: SyntaxError,
      parse:       parse
    };
  })();
  
  },{"lodash":28}],5:[function(require,module,exports){
  var _ = require("lodash"),
      grammar = require("./dot-grammar"),
      buildGraph = require("./build-graph");
  
  module.exports = function readMany(str) {
    var parseTree = grammar.parse(str);
    return _.map(parseTree, buildGraph);
  };
  
  },{"./build-graph":3,"./dot-grammar":4,"lodash":28}],6:[function(require,module,exports){
  var grammar = require("./dot-grammar"),
      buildGraph = require("./build-graph");
  
  module.exports = function readOne(str) {
    var parseTree = grammar.parse(str, { startRule: "graphStmt" });
    return buildGraph(parseTree);
  };
  
  
  },{"./build-graph":3,"./dot-grammar":4}],7:[function(require,module,exports){
  module.exports = '0.5.2';
  
  },{}],8:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = writeOne;
  
  var UNESCAPED_ID_PATTERN = /^[a-zA-Z\200-\377_][a-zA-Z\200-\377_0-9]*$/;
  
  function writeOne(g) {
    var ec = g.isDirected() ? "->" : "--",
        writer = new Writer();
  
    if (!g.isMultigraph()) {
      writer.write("strict ");
    }
  
    writer.writeLine((g.isDirected() ? "digraph" : "graph") + " {");
    writer.indent();
  
    var graphAttrs = g.graph();
    if (_.isObject(graphAttrs)) {
      _.each(graphAttrs, function(v, k) {
        writer.writeLine(id(k) + "=" + id(v) + ";");
      });
    }
  
    writeSubgraph(g, undefined, writer);
  
    g.edges().forEach(function(edge) {
      writeEdge(g, edge, ec, writer);
    });
  
    writer.unindent();
    writer.writeLine("}");
  
    return writer.toString();
  }
  
  function writeSubgraph(g, v, writer) {
    var children = g.isCompound() ? g.children(v) : g.nodes();
    _.each(children, function(w) {
      if (!g.isCompound() || !g.children(w).length) {
        writeNode(g, w, writer);
      } else {
        writer.writeLine("subgraph " + id(w) + " {");
        writer.indent();
  
        if (_.isObject(g.node(w))) {
          _.map(g.node(w), function(val, key) {
            writer.writeLine(id(key) + "=" + id(val) + ";");
          });
        }
  
        writeSubgraph(g, w, writer);
        writer.unindent();
        writer.writeLine("}");
      }
    });
  }
  
  function writeNode(g, v, writer) {
    writer.write(id(v));
    writeAttrs(g.node(v), writer);
    writer.writeLine();
  }
  
  function writeEdge(g, edge, ec, writer) {
    var v = edge.v,
        w = edge.w,
        attrs = g.edge(edge);
  
    writer.write(id(v) + " " + ec + " " + id(w));
    writeAttrs(attrs, writer);
    writer.writeLine();
  }
  
  function writeAttrs(attrs, writer) {
    if (_.isObject(attrs)) {
      var attrStrs = _.map(attrs, function(val, key) {
        return id(key) + "=" + id(val);
      });
      if (attrStrs.length) {
        writer.write(" [" + attrStrs.join(",") + "]");
      }
    }
  }
  
  function id(obj) {
    if (typeof obj === "number" || obj.toString().match(UNESCAPED_ID_PATTERN)) {
      return obj;
    }
  
    return "\"" + obj.toString().replace(/"/g, "\\\"") + "\"";
  }
  
  // Helper object for making a pretty printer
  function Writer() {
    this._indent = "";
    this._content = "";
    this._shouldIndent = true;
  }
  
  Writer.prototype.INDENT = "  ";
  
  Writer.prototype.indent = function() {
    this._indent += this.INDENT;
  };
  
  Writer.prototype.unindent = function() {
    this._indent = this._indent.slice(this.INDENT.length);
  };
  
  Writer.prototype.writeLine = function(line) {
    this.write((line || "") + "\n");
    this._shouldIndent = true;
  };
  
  Writer.prototype.write = function(str) {
    if (this._shouldIndent) {
      this._shouldIndent = false;
      this._content += this._indent;
    }
    this._content += str;
  };
  
  Writer.prototype.toString = function() {
    return this._content;
  };
  
  
  },{"lodash":28}],9:[function(require,module,exports){
  /**
   * Copyright (c) 2014, Chris Pettitt
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   * list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   * this list of conditions and the following disclaimer in the documentation
   * and/or other materials provided with the distribution.
   *
   * 3. Neither the name of the copyright holder nor the names of its contributors
   * may be used to endorse or promote products derived from this software without
   * specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  var _ = require("lodash");
  
  module.exports = _.clone(require("./lib"));
  module.exports.json = require("./lib/json");
  module.exports.alg = require("./lib/alg");
  
  },{"./lib":25,"./lib/alg":16,"./lib/json":26,"lodash":28}],10:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = components;
  
  function components(g) {
    var visited = {},
        cmpts = [],
        cmpt;
  
    function dfs(v) {
      if (_.has(visited, v)) return;
      visited[v] = true;
      cmpt.push(v);
      _.each(g.successors(v), dfs);
      _.each(g.predecessors(v), dfs);
    }
  
    _.each(g.nodes(), function(v) {
      cmpt = [];
      dfs(v);
      if (cmpt.length) {
        cmpts.push(cmpt);
      }
    });
  
    return cmpts;
  }
  
  },{"lodash":28}],11:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = dfs;
  
  /*
   * A helper that preforms a pre- or post-order traversal on the input graph
   * and returns the nodes in the order they were visited. This algorithm treats
   * the input as undirected.
   *
   * Order must be one of "pre" or "post".
   */
  function dfs(g, vs, order) {
    if (!_.isArray(vs)) {
      vs = [vs];
    }
  
    var acc = [],
        visited = {};
    _.each(vs, function(v) {
      if (!g.hasNode(v)) {
        throw new Error("Graph does not have node: " + v);
      }
  
      doDfs(g, v, order === "post", visited, acc);
    });
    return acc;
  }
  
  function doDfs(g, v, postorder, visited, acc) {
    if (!_.has(visited, v)) {
      visited[v] = true;
  
      if (!postorder) { acc.push(v); }
      _.each(g.neighbors(v), function(w) {
        doDfs(g, w, postorder, visited, acc);
      });
      if (postorder) { acc.push(v); }
    }
  }
  
  },{"lodash":28}],12:[function(require,module,exports){
  var dijkstra = require("./dijkstra"),
      _ = require("lodash");
  
  module.exports = dijkstraAll;
  
  function dijkstraAll(g, weightFunc, edgeFunc) {
    return _.transform(g.nodes(), function(acc, v) {
      acc[v] = dijkstra(g, v, weightFunc, edgeFunc);
    }, {});
  }
  
  },{"./dijkstra":13,"lodash":28}],13:[function(require,module,exports){
  var _ = require("lodash"),
      PriorityQueue = require("../data/priority-queue");
  
  module.exports = dijkstra;
  
  var DEFAULT_WEIGHT_FUNC = _.constant(1);
  
  function dijkstra(g, source, weightFn, edgeFn) {
    return runDijkstra(g, String(source),
                       weightFn || DEFAULT_WEIGHT_FUNC,
                       edgeFn || function(v) { return g.outEdges(v); });
  }
  
  function runDijkstra(g, source, weightFn, edgeFn) {
    var results = {},
        pq = new PriorityQueue(),
        v, vEntry;
  
    var updateNeighbors = function(edge) {
      var w = edge.v !== v ? edge.v : edge.w,
          wEntry = results[w],
          weight = weightFn(edge),
          distance = vEntry.distance + weight;
  
      if (weight < 0) {
        throw new Error("dijkstra does not allow negative edge weights. " +
                        "Bad edge: " + edge + " Weight: " + weight);
      }
  
      if (distance < wEntry.distance) {
        wEntry.distance = distance;
        wEntry.predecessor = v;
        pq.decrease(w, distance);
      }
    };
  
    g.nodes().forEach(function(v) {
      var distance = v === source ? 0 : Number.POSITIVE_INFINITY;
      results[v] = { distance: distance };
      pq.add(v, distance);
    });
  
    while (pq.size() > 0) {
      v = pq.removeMin();
      vEntry = results[v];
      if (vEntry.distance === Number.POSITIVE_INFINITY) {
        break;
      }
  
      edgeFn(v).forEach(updateNeighbors);
    }
  
    return results;
  }
  
  },{"../data/priority-queue":23,"lodash":28}],14:[function(require,module,exports){
  var _ = require("lodash"),
      tarjan = require("./tarjan");
  
  module.exports = findCycles;
  
  function findCycles(g) {
    return _.filter(tarjan(g), function(cmpt) { return cmpt.length > 1; });
  }
  
  },{"./tarjan":21,"lodash":28}],15:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = floydWarshall;
  
  var DEFAULT_WEIGHT_FUNC = _.constant(1);
  
  function floydWarshall(g, weightFn, edgeFn) {
    return runFloydWarshall(g,
                            weightFn || DEFAULT_WEIGHT_FUNC,
                            edgeFn || function(v) { return g.outEdges(v); });
  }
  
  function runFloydWarshall(g, weightFn, edgeFn) {
    var results = {},
        nodes = g.nodes();
  
    nodes.forEach(function(v) {
      results[v] = {};
      results[v][v] = { distance: 0 };
      nodes.forEach(function(w) {
        if (v !== w) {
          results[v][w] = { distance: Number.POSITIVE_INFINITY };
        }
      });
      edgeFn(v).forEach(function(edge) {
        var w = edge.v === v ? edge.w : edge.v,
            d = weightFn(edge);
        results[v][w] = { distance: d, predecessor: v };
      });
    });
  
    nodes.forEach(function(k) {
      var rowK = results[k];
      nodes.forEach(function(i) {
        var rowI = results[i];
        nodes.forEach(function(j) {
          var ik = rowI[k];
          var kj = rowK[j];
          var ij = rowI[j];
          var altDistance = ik.distance + kj.distance;
          if (altDistance < ij.distance) {
            ij.distance = altDistance;
            ij.predecessor = kj.predecessor;
          }
        });
      });
    });
  
    return results;
  }
  
  },{"lodash":28}],16:[function(require,module,exports){
  module.exports = {
    components: require("./components"),
    dijkstra: require("./dijkstra"),
    dijkstraAll: require("./dijkstra-all"),
    findCycles: require("./find-cycles"),
    floydWarshall: require("./floyd-warshall"),
    isAcyclic: require("./is-acyclic"),
    postorder: require("./postorder"),
    preorder: require("./preorder"),
    prim: require("./prim"),
    tarjan: require("./tarjan"),
    topsort: require("./topsort")
  };
  
  },{"./components":10,"./dijkstra":13,"./dijkstra-all":12,"./find-cycles":14,"./floyd-warshall":15,"./is-acyclic":17,"./postorder":18,"./preorder":19,"./prim":20,"./tarjan":21,"./topsort":22}],17:[function(require,module,exports){
  var topsort = require("./topsort");
  
  module.exports = isAcyclic;
  
  function isAcyclic(g) {
    try {
      topsort(g);
    } catch (e) {
      if (e instanceof topsort.CycleException) {
        return false;
      }
      throw e;
    }
    return true;
  }
  
  },{"./topsort":22}],18:[function(require,module,exports){
  var dfs = require("./dfs");
  
  module.exports = postorder;
  
  function postorder(g, vs) {
    return dfs(g, vs, "post");
  }
  
  },{"./dfs":11}],19:[function(require,module,exports){
  var dfs = require("./dfs");
  
  module.exports = preorder;
  
  function preorder(g, vs) {
    return dfs(g, vs, "pre");
  }
  
  },{"./dfs":11}],20:[function(require,module,exports){
  var _ = require("lodash"),
      Graph = require("../graph"),
      PriorityQueue = require("../data/priority-queue");
  
  module.exports = prim;
  
  function prim(g, weightFunc) {
    var result = new Graph(),
        parents = {},
        pq = new PriorityQueue(),
        v;
  
    function updateNeighbors(edge) {
      var w = edge.v === v ? edge.w : edge.v,
          pri = pq.priority(w);
      if (pri !== undefined) {
        var edgeWeight = weightFunc(edge);
        if (edgeWeight < pri) {
          parents[w] = v;
          pq.decrease(w, edgeWeight);
        }
      }
    }
  
    if (g.nodeCount() === 0) {
      return result;
    }
  
    _.each(g.nodes(), function(v) {
      pq.add(v, Number.POSITIVE_INFINITY);
      result.setNode(v);
    });
  
    // Start from an arbitrary node
    pq.decrease(g.nodes()[0], 0);
  
    var init = false;
    while (pq.size() > 0) {
      v = pq.removeMin();
      if (_.has(parents, v)) {
        result.setEdge(v, parents[v]);
      } else if (init) {
        throw new Error("Input graph is not connected: " + g);
      } else {
        init = true;
      }
  
      g.nodeEdges(v).forEach(updateNeighbors);
    }
  
    return result;
  }
  
  },{"../data/priority-queue":23,"../graph":24,"lodash":28}],21:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = tarjan;
  
  function tarjan(g) {
    var index = 0,
        stack = [],
        visited = {}, // node id -> { onStack, lowlink, index }
        results = [];
  
    function dfs(v) {
      var entry = visited[v] = {
        onStack: true,
        lowlink: index,
        index: index++
      };
      stack.push(v);
  
      g.successors(v).forEach(function(w) {
        if (!_.has(visited, w)) {
          dfs(w);
          entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink);
        } else if (visited[w].onStack) {
          entry.lowlink = Math.min(entry.lowlink, visited[w].index);
        }
      });
  
      if (entry.lowlink === entry.index) {
        var cmpt = [],
            w;
        do {
          w = stack.pop();
          visited[w].onStack = false;
          cmpt.push(w);
        } while (v !== w);
        results.push(cmpt);
      }
    }
  
    g.nodes().forEach(function(v) {
      if (!_.has(visited, v)) {
        dfs(v);
      }
    });
  
    return results;
  }
  
  },{"lodash":28}],22:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = topsort;
  topsort.CycleException = CycleException;
  
  function topsort(g) {
    var visited = {},
        stack = {},
        results = [];
  
    function visit(node) {
      if (_.has(stack, node)) {
        throw new CycleException();
      }
  
      if (!_.has(visited, node)) {
        stack[node] = true;
        visited[node] = true;
        _.each(g.predecessors(node), visit);
        delete stack[node];
        results.push(node);
      }
    }
  
    _.each(g.sinks(), visit);
  
    if (_.size(visited) !== g.nodeCount()) {
      throw new CycleException();
    }
  
    return results;
  }
  
  function CycleException() {}
  
  },{"lodash":28}],23:[function(require,module,exports){
  var _ = require("lodash");
  
  module.exports = PriorityQueue;
  
  /**
   * A min-priority queue data structure. This algorithm is derived from Cormen,
   * et al., "Introduction to Algorithms". The basic idea of a min-priority
   * queue is that you can efficiently (in O(1) time) get the smallest key in
   * the queue. Adding and removing elements takes O(log n) time. A key can
   * have its priority decreased in O(log n) time.
   */
  function PriorityQueue() {
    this._arr = [];
    this._keyIndices = {};
  }
  
  /**
   * Returns the number of elements in the queue. Takes `O(1)` time.
   */
  PriorityQueue.prototype.size = function() {
    return this._arr.length;
  };
  
  /**
   * Returns the keys that are in the queue. Takes `O(n)` time.
   */
  PriorityQueue.prototype.keys = function() {
    return this._arr.map(function(x) { return x.key; });
  };
  
  /**
   * Returns `true` if **key** is in the queue and `false` if not.
   */
  PriorityQueue.prototype.has = function(key) {
    return _.has(this._keyIndices, key);
  };
  
  /**
   * Returns the priority for **key**. If **key** is not present in the queue
   * then this function returns `undefined`. Takes `O(1)` time.
   *
   * @param {Object} key
   */
  PriorityQueue.prototype.priority = function(key) {
    var index = this._keyIndices[key];
    if (index !== undefined) {
      return this._arr[index].priority;
    }
  };
  
  /**
   * Returns the key for the minimum element in this queue. If the queue is
   * empty this function throws an Error. Takes `O(1)` time.
   */
  PriorityQueue.prototype.min = function() {
    if (this.size() === 0) {
      throw new Error("Queue underflow");
    }
    return this._arr[0].key;
  };
  
  /**
   * Inserts a new key into the priority queue. If the key already exists in
   * the queue this function returns `false`; otherwise it will return `true`.
   * Takes `O(n)` time.
   *
   * @param {Object} key the key to add
   * @param {Number} priority the initial priority for the key
   */
  PriorityQueue.prototype.add = function(key, priority) {
    var keyIndices = this._keyIndices;
    key = String(key);
    if (!_.has(keyIndices, key)) {
      var arr = this._arr;
      var index = arr.length;
      keyIndices[key] = index;
      arr.push({key: key, priority: priority});
      this._decrease(index);
      return true;
    }
    return false;
  };
  
  /**
   * Removes and returns the smallest key in the queue. Takes `O(log n)` time.
   */
  PriorityQueue.prototype.removeMin = function() {
    this._swap(0, this._arr.length - 1);
    var min = this._arr.pop();
    delete this._keyIndices[min.key];
    this._heapify(0);
    return min.key;
  };
  
  /**
   * Decreases the priority for **key** to **priority**. If the new priority is
   * greater than the previous priority, this function will throw an Error.
   *
   * @param {Object} key the key for which to raise priority
   * @param {Number} priority the new priority for the key
   */
  PriorityQueue.prototype.decrease = function(key, priority) {
    var index = this._keyIndices[key];
    if (priority > this._arr[index].priority) {
      throw new Error("New priority is greater than current priority. " +
          "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority);
    }
    this._arr[index].priority = priority;
    this._decrease(index);
  };
  
  PriorityQueue.prototype._heapify = function(i) {
    var arr = this._arr;
    var l = 2 * i,
        r = l + 1,
        largest = i;
    if (l < arr.length) {
      largest = arr[l].priority < arr[largest].priority ? l : largest;
      if (r < arr.length) {
        largest = arr[r].priority < arr[largest].priority ? r : largest;
      }
      if (largest !== i) {
        this._swap(i, largest);
        this._heapify(largest);
      }
    }
  };
  
  PriorityQueue.prototype._decrease = function(index) {
    var arr = this._arr;
    var priority = arr[index].priority;
    var parent;
    while (index !== 0) {
      parent = index >> 1;
      if (arr[parent].priority < priority) {
        break;
      }
      this._swap(index, parent);
      index = parent;
    }
  };
  
  PriorityQueue.prototype._swap = function(i, j) {
    var arr = this._arr;
    var keyIndices = this._keyIndices;
    var origArrI = arr[i];
    var origArrJ = arr[j];
    arr[i] = origArrJ;
    arr[j] = origArrI;
    keyIndices[origArrJ.key] = i;
    keyIndices[origArrI.key] = j;
  };
  
  },{"lodash":28}],24:[function(require,module,exports){
  "use strict";
  
  var _ = require("lodash");
  
  module.exports = Graph;
  
  var DEFAULT_EDGE_NAME = "\x00",
      GRAPH_NODE = "\x00",
      EDGE_KEY_DELIM = "\x01";
  
  // Implementation notes:
  //
  //  * Node id query functions should return string ids for the nodes
  //  * Edge id query functions should return an "edgeObj", edge object, that is
  //    composed of enough information to uniquely identify an edge: {v, w, name}.
  //  * Internally we use an "edgeId", a stringified form of the edgeObj, to
  //    reference edges. This is because we need a performant way to look these
  //    edges up and, object properties, which have string keys, are the closest
  //    we're going to get to a performant hashtable in JavaScript.
  
  function Graph(opts) {
    this._isDirected = _.has(opts, "directed") ? opts.directed : true;
    this._isMultigraph = _.has(opts, "multigraph") ? opts.multigraph : false;
    this._isCompound = _.has(opts, "compound") ? opts.compound : false;
  
    // Label for the graph itself
    this._label = undefined;
  
    // Defaults to be set when creating a new node
    this._defaultNodeLabelFn = _.constant(undefined);
  
    // Defaults to be set when creating a new edge
    this._defaultEdgeLabelFn = _.constant(undefined);
  
    // v -> label
    this._nodes = {};
  
    if (this._isCompound) {
      // v -> parent
      this._parent = {};
  
      // v -> children
      this._children = {};
      this._children[GRAPH_NODE] = {};
    }
  
    // v -> edgeObj
    this._in = {};
  
    // u -> v -> Number
    this._preds = {};
  
    // v -> edgeObj
    this._out = {};
  
    // v -> w -> Number
    this._sucs = {};
  
    // e -> edgeObj
    this._edgeObjs = {};
  
    // e -> label
    this._edgeLabels = {};
  }
  
  /* Number of nodes in the graph. Should only be changed by the implementation. */
  Graph.prototype._nodeCount = 0;
  
  /* Number of edges in the graph. Should only be changed by the implementation. */
  Graph.prototype._edgeCount = 0;
  
  
  /* === Graph functions ========= */
  
  Graph.prototype.isDirected = function() {
    return this._isDirected;
  };
  
  Graph.prototype.isMultigraph = function() {
    return this._isMultigraph;
  };
  
  Graph.prototype.isCompound = function() {
    return this._isCompound;
  };
  
  Graph.prototype.setGraph = function(label) {
    this._label = label;
    return this;
  };
  
  Graph.prototype.graph = function() {
    return this._label;
  };
  
  
  /* === Node functions ========== */
  
  Graph.prototype.setDefaultNodeLabel = function(newDefault) {
    if (!_.isFunction(newDefault)) {
      newDefault = _.constant(newDefault);
    }
    this._defaultNodeLabelFn = newDefault;
    return this;
  };
  
  Graph.prototype.nodeCount = function() {
    return this._nodeCount;
  };
  
  Graph.prototype.nodes = function() {
    return _.keys(this._nodes);
  };
  
  Graph.prototype.sources = function() {
    return _.filter(this.nodes(), function(v) {
      return _.isEmpty(this._in[v]);
    }, this);
  };
  
  Graph.prototype.sinks = function() {
    return _.filter(this.nodes(), function(v) {
      return _.isEmpty(this._out[v]);
    }, this);
  };
  
  Graph.prototype.setNodes = function(vs, value) {
    var args = arguments;
    _.each(vs, function(v) {
      if (args.length > 1) {
        this.setNode(v, value);
      } else {
        this.setNode(v);
      }
    }, this);
    return this;
  };
  
  Graph.prototype.setNode = function(v, value) {
    if (_.has(this._nodes, v)) {
      if (arguments.length > 1) {
        this._nodes[v] = value;
      }
      return this;
    }
  
    this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v);
    if (this._isCompound) {
      this._parent[v] = GRAPH_NODE;
      this._children[v] = {};
      this._children[GRAPH_NODE][v] = true;
    }
    this._in[v] = {};
    this._preds[v] = {};
    this._out[v] = {};
    this._sucs[v] = {};
    ++this._nodeCount;
    return this;
  };
  
  Graph.prototype.node = function(v) {
    return this._nodes[v];
  };
  
  Graph.prototype.hasNode = function(v) {
    return _.has(this._nodes, v);
  };
  
  Graph.prototype.removeNode =  function(v) {
    var self = this;
    if (_.has(this._nodes, v)) {
      var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); };
      delete this._nodes[v];
      if (this._isCompound) {
        this._removeFromParentsChildList(v);
        delete this._parent[v];
        _.each(this.children(v), function(child) {
          this.setParent(child);
        }, this);
        delete this._children[v];
      }
      _.each(_.keys(this._in[v]), removeEdge);
      delete this._in[v];
      delete this._preds[v];
      _.each(_.keys(this._out[v]), removeEdge);
      delete this._out[v];
      delete this._sucs[v];
      --this._nodeCount;
    }
    return this;
  };
  
  Graph.prototype.setParent = function(v, parent) {
    if (!this._isCompound) {
      throw new Error("Cannot set parent in a non-compound graph");
    }
  
    if (_.isUndefined(parent)) {
      parent = GRAPH_NODE;
    } else {
      for (var ancestor = parent;
           !_.isUndefined(ancestor);
           ancestor = this.parent(ancestor)) {
        if (ancestor === v) {
          throw new Error("Setting " + parent+ " as parent of " + v +
                          " would create create a cycle");
        }
      }
  
      this.setNode(parent);
    }
  
    this.setNode(v);
    this._removeFromParentsChildList(v);
    this._parent[v] = parent;
    this._children[parent][v] = true;
    return this;
  };
  
  Graph.prototype._removeFromParentsChildList = function(v) {
    delete this._children[this._parent[v]][v];
  };
  
  Graph.prototype.parent = function(v) {
    if (this._isCompound) {
      var parent = this._parent[v];
      if (parent !== GRAPH_NODE) {
        return parent;
      }
    }
  };
  
  Graph.prototype.children = function(v) {
    if (_.isUndefined(v)) {
      v = GRAPH_NODE;
    }
  
    if (this._isCompound) {
      var children = this._children[v];
      if (children) {
        return _.keys(children);
      }
    } else if (v === GRAPH_NODE) {
      return this.nodes();
    } else if (this.hasNode(v)) {
      return [];
    }
  };
  
  Graph.prototype.predecessors = function(v) {
    var predsV = this._preds[v];
    if (predsV) {
      return _.keys(predsV);
    }
  };
  
  Graph.prototype.successors = function(v) {
    var sucsV = this._sucs[v];
    if (sucsV) {
      return _.keys(sucsV);
    }
  };
  
  Graph.prototype.neighbors = function(v) {
    var preds = this.predecessors(v);
    if (preds) {
      return _.union(preds, this.successors(v));
    }
  };
  
  /* === Edge functions ========== */
  
  Graph.prototype.setDefaultEdgeLabel = function(newDefault) {
    if (!_.isFunction(newDefault)) {
      newDefault = _.constant(newDefault);
    }
    this._defaultEdgeLabelFn = newDefault;
    return this;
  };
  
  Graph.prototype.edgeCount = function() {
    return this._edgeCount;
  };
  
  Graph.prototype.edges = function() {
    return _.values(this._edgeObjs);
  };
  
  Graph.prototype.setPath = function(vs, value) {
    var self = this,
        args = arguments;
    _.reduce(vs, function(v, w) {
      if (args.length > 1) {
        self.setEdge(v, w, value);
      } else {
        self.setEdge(v, w);
      }
      return w;
    });
    return this;
  };
  
  /*
   * setEdge(v, w, [value, [name]])
   * setEdge({ v, w, [name] }, [value])
   */
  Graph.prototype.setEdge = function(v, w, value, name) {
    var valueSpecified = arguments.length > 2;
  
    if (_.isPlainObject(arguments[0])) {
      v = arguments[0].v;
      w = arguments[0].w;
      name = arguments[0].name;
      if (arguments.length === 2) {
        value = arguments[1];
        valueSpecified = true;
      }
    }
  
    var e = edgeArgsToId(this._isDirected, v, w, name);
    if (_.has(this._edgeLabels, e)) {
      if (valueSpecified) {
        this._edgeLabels[e] = value;
      }
      return this;
    }
  
    if (!_.isUndefined(name) && !this._isMultigraph) {
      throw new Error("Cannot set a named edge when isMultigraph = false");
    }
  
    // It didn't exist, so we need to create it.
    // First ensure the nodes exist.
    this.setNode(v);
    this.setNode(w);
  
    this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name);
  
    var edgeObj = edgeArgsToObj(this._isDirected, v, w, name);
    // Ensure we add undirected edges in a consistent way.
    v = edgeObj.v;
    w = edgeObj.w;
  
    Object.freeze(edgeObj);
    this._edgeObjs[e] = edgeObj;
    incrementOrInitEntry(this._preds[w], v);
    incrementOrInitEntry(this._sucs[v], w);
    this._in[w][e] = edgeObj;
    this._out[v][e] = edgeObj;
    this._edgeCount++;
    return this;
  };
  
  Graph.prototype.edge = function(v, w, name) {
    var e = (arguments.length === 1
              ? edgeObjToId(this._isDirected, arguments[0])
              : edgeArgsToId(this._isDirected, v, w, name));
    return this._edgeLabels[e];
  };
  
  Graph.prototype.hasEdge = function(v, w, name) {
    var e = (arguments.length === 1
              ? edgeObjToId(this._isDirected, arguments[0])
              : edgeArgsToId(this._isDirected, v, w, name));
    return _.has(this._edgeLabels, e);
  };
  
  Graph.prototype.removeEdge = function(v, w, name) {
    var e = (arguments.length === 1
              ? edgeObjToId(this._isDirected, arguments[0])
              : edgeArgsToId(this._isDirected, v, w, name)),
        edge = this._edgeObjs[e];
    if (edge) {
      v = edge.v;
      w = edge.w;
      delete this._edgeLabels[e];
      delete this._edgeObjs[e];
      decrementOrRemoveEntry(this._preds[w], v);
      decrementOrRemoveEntry(this._sucs[v], w);
      delete this._in[w][e];
      delete this._out[v][e];
      this._edgeCount--;
    }
    return this;
  };
  
  Graph.prototype.inEdges = function(v, u) {
    var inV = this._in[v];
    if (inV) {
      var edges = _.values(inV);
      if (!u) {
        return edges;
      }
      return _.filter(edges, function(edge) { return edge.v === u; });
    }
  };
  
  Graph.prototype.outEdges = function(v, w) {
    var outV = this._out[v];
    if (outV) {
      var edges = _.values(outV);
      if (!w) {
        return edges;
      }
      return _.filter(edges, function(edge) { return edge.w === w; });
    }
  };
  
  Graph.prototype.nodeEdges = function(v, w) {
    var inEdges = this.inEdges(v, w);
    if (inEdges) {
      return inEdges.concat(this.outEdges(v, w));
    }
  };
  
  function incrementOrInitEntry(map, k) {
    if (_.has(map, k)) {
      map[k]++;
    } else {
      map[k] = 1;
    }
  }
  
  function decrementOrRemoveEntry(map, k) {
    if (!--map[k]) { delete map[k]; }
  }
  
  function edgeArgsToId(isDirected, v, w, name) {
    if (!isDirected && v > w) {
      var tmp = v;
      v = w;
      w = tmp;
    }
    return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM +
               (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name);
  }
  
  function edgeArgsToObj(isDirected, v, w, name) {
    if (!isDirected && v > w) {
      var tmp = v;
      v = w;
      w = tmp;
    }
    var edgeObj =  { v: v, w: w };
    if (name) {
      edgeObj.name = name;
    }
    return edgeObj;
  }
  
  function edgeObjToId(isDirected, edgeObj) {
    return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name);
  }
  
  },{"lodash":28}],25:[function(require,module,exports){
  // Includes only the "core" of graphlib
  module.exports = {
    Graph: require("./graph"),
    version: require("./version")
  };
  
  },{"./graph":24,"./version":27}],26:[function(require,module,exports){
  var _ = require("lodash"),
      Graph = require("./graph");
  
  module.exports = {
    write: write,
    read: read
  };
  
  function write(g) {
    var json = {
      options: {
        directed: g.isDirected(),
        multigraph: g.isMultigraph(),
        compound: g.isCompound()
      },
      nodes: writeNodes(g),
      edges: writeEdges(g)
    };
    if (!_.isUndefined(g.graph())) {
      json.value = _.clone(g.graph());
    }
    return json;
  }
  
  function writeNodes(g) {
    return _.map(g.nodes(), function(v) {
      var nodeValue = g.node(v),
          parent = g.parent(v),
          node = { v: v };
      if (!_.isUndefined(nodeValue)) {
        node.value = nodeValue;
      }
      if (!_.isUndefined(parent)) {
        node.parent = parent;
      }
      return node;
    });
  }
  
  function writeEdges(g) {
    return _.map(g.edges(), function(e) {
      var edgeValue = g.edge(e),
          edge = { v: e.v, w: e.w };
      if (!_.isUndefined(e.name)) {
        edge.name = e.name;
      }
      if (!_.isUndefined(edgeValue)) {
        edge.value = edgeValue;
      }
      return edge;
    });
  }
  
  function read(json) {
    var g = new Graph(json.options).setGraph(json.value);
    _.each(json.nodes, function(entry) {
      g.setNode(entry.v, entry.value);
      if (entry.parent) {
        g.setParent(entry.v, entry.parent);
      }
    });
    _.each(json.edges, function(entry) {
      g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value);
    });
    return g;
  }
  
  },{"./graph":24,"lodash":28}],27:[function(require,module,exports){
  module.exports = '0.8.1';
  
  },{}],28:[function(require,module,exports){
  (function (global){
  /**
   * @license
   * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
   * Build: `lodash modern -o ./dist/lodash.js`
   * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
   * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
   * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
   * Available under MIT license <http://lodash.com/license>
   */
  ;(function() {
  
    /** Used as a safe reference for `undefined` in pre ES5 environments */
    var undefined;
  
    /** Used to pool arrays and objects used internally */
    var arrayPool = [],
        objectPool = [];
  
    /** Used to generate unique IDs */
    var idCounter = 0;
  
    /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
    var keyPrefix = +new Date + '';
  
    /** Used as the size when optimizations are enabled for large arrays */
    var largeArraySize = 75;
  
    /** Used as the max size of the `arrayPool` and `objectPool` */
    var maxPoolSize = 40;
  
    /** Used to detect and test whitespace */
    var whitespace = (
      // whitespace
      ' \t\x0B\f\xA0\ufeff' +
  
      // line terminators
      '\n\r\u2028\u2029' +
  
      // unicode category "Zs" space separators
      '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
    );
  
    /** Used to match empty string literals in compiled template source */
    var reEmptyStringLeading = /\b__p \+= '';/g,
        reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
        reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
  
    /**
     * Used to match ES6 template delimiters
     * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
     */
    var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
  
    /** Used to match regexp flags from their coerced string values */
    var reFlags = /\w*$/;
  
    /** Used to detected named functions */
    var reFuncName = /^\s*function[ \n\r\t]+\w/;
  
    /** Used to match "interpolate" template delimiters */
    var reInterpolate = /<%=([\s\S]+?)%>/g;
  
    /** Used to match leading whitespace and zeros to be removed */
    var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
  
    /** Used to ensure capturing order of template delimiters */
    var reNoMatch = /($^)/;
  
    /** Used to detect functions containing a `this` reference */
    var reThis = /\bthis\b/;
  
    /** Used to match unescaped characters in compiled string literals */
    var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
  
    /** Used to assign default `context` object properties */
    var contextProps = [
      'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
      'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
      'parseInt', 'setTimeout'
    ];
  
    /** Used to make template sourceURLs easier to identify */
    var templateCounter = 0;
  
    /** `Object#toString` result shortcuts */
    var argsClass = '[object Arguments]',
        arrayClass = '[object Array]',
        boolClass = '[object Boolean]',
        dateClass = '[object Date]',
        funcClass = '[object Function]',
        numberClass = '[object Number]',
        objectClass = '[object Object]',
        regexpClass = '[object RegExp]',
        stringClass = '[object String]';
  
    /** Used to identify object classifications that `_.clone` supports */
    var cloneableClasses = {};
    cloneableClasses[funcClass] = false;
    cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
    cloneableClasses[boolClass] = cloneableClasses[dateClass] =
    cloneableClasses[numberClass] = cloneableClasses[objectClass] =
    cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
  
    /** Used as an internal `_.debounce` options object */
    var debounceOptions = {
      'leading': false,
      'maxWait': 0,
      'trailing': false
    };
  
    /** Used as the property descriptor for `__bindData__` */
    var descriptor = {
      'configurable': false,
      'enumerable': false,
      'value': null,
      'writable': false
    };
  
    /** Used to determine if values are of the language type Object */
    var objectTypes = {
      'boolean': false,
      'function': true,
      'object': true,
      'number': false,
      'string': false,
      'undefined': false
    };
  
    /** Used to escape characters for inclusion in compiled string literals */
    var stringEscapes = {
      '\\': '\\',
      "'": "'",
      '\n': 'n',
      '\r': 'r',
      '\t': 't',
      '\u2028': 'u2028',
      '\u2029': 'u2029'
    };
  
    /** Used as a reference to the global object */
    var root = (objectTypes[typeof window] && window) || this;
  
    /** Detect free variable `exports` */
    var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
  
    /** Detect free variable `module` */
    var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
  
    /** Detect the popular CommonJS extension `module.exports` */
    var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
  
    /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
    var freeGlobal = objectTypes[typeof global] && global;
    if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
      root = freeGlobal;
    }
  
    /*--------------------------------------------------------------------------*/
  
    /**
     * The base implementation of `_.indexOf` without support for binary searches
     * or `fromIndex` constraints.
     *
     * @private
     * @param {Array} array The array to search.
     * @param {*} value The value to search for.
     * @param {number} [fromIndex=0] The index to search from.
     * @returns {number} Returns the index of the matched value or `-1`.
     */
    function baseIndexOf(array, value, fromIndex) {
      var index = (fromIndex || 0) - 1,
          length = array ? array.length : 0;
  
      while (++index < length) {
        if (array[index] === value) {
          return index;
        }
      }
      return -1;
    }
  
    /**
     * An implementation of `_.contains` for cache objects that mimics the return
     * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
     *
     * @private
     * @param {Object} cache The cache object to inspect.
     * @param {*} value The value to search for.
     * @returns {number} Returns `0` if `value` is found, else `-1`.
     */
    function cacheIndexOf(cache, value) {
      var type = typeof value;
      cache = cache.cache;
  
      if (type == 'boolean' || value == null) {
        return cache[value] ? 0 : -1;
      }
      if (type != 'number' && type != 'string') {
        type = 'object';
      }
      var key = type == 'number' ? value : keyPrefix + value;
      cache = (cache = cache[type]) && cache[key];
  
      return type == 'object'
        ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
        : (cache ? 0 : -1);
    }
  
    /**
     * Adds a given value to the corresponding cache object.
     *
     * @private
     * @param {*} value The value to add to the cache.
     */
    function cachePush(value) {
      var cache = this.cache,
          type = typeof value;
  
      if (type == 'boolean' || value == null) {
        cache[value] = true;
      } else {
        if (type != 'number' && type != 'string') {
          type = 'object';
        }
        var key = type == 'number' ? value : keyPrefix + value,
            typeCache = cache[type] || (cache[type] = {});
  
        if (type == 'object') {
          (typeCache[key] || (typeCache[key] = [])).push(value);
        } else {
          typeCache[key] = true;
        }
      }
    }
  
    /**
     * Used by `_.max` and `_.min` as the default callback when a given
     * collection is a string value.
     *
     * @private
     * @param {string} value The character to inspect.
     * @returns {number} Returns the code unit of given character.
     */
    function charAtCallback(value) {
      return value.charCodeAt(0);
    }
  
    /**
     * Used by `sortBy` to compare transformed `collection` elements, stable sorting
     * them in ascending order.
     *
     * @private
     * @param {Object} a The object to compare to `b`.
     * @param {Object} b The object to compare to `a`.
     * @returns {number} Returns the sort order indicator of `1` or `-1`.
     */
    function compareAscending(a, b) {
      var ac = a.criteria,
          bc = b.criteria,
          index = -1,
          length = ac.length;
  
      while (++index < length) {
        var value = ac[index],
            other = bc[index];
  
        if (value !== other) {
          if (value > other || typeof value == 'undefined') {
            return 1;
          }
          if (value < other || typeof other == 'undefined') {
            return -1;
          }
        }
      }
      // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
      // that causes it, under certain circumstances, to return the same value for
      // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
      //
      // This also ensures a stable sort in V8 and other engines.
      // See http://code.google.com/p/v8/issues/detail?id=90
      return a.index - b.index;
    }
  
    /**
     * Creates a cache object to optimize linear searches of large arrays.
     *
     * @private
     * @param {Array} [array=[]] The array to search.
     * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
     */
    function createCache(array) {
      var index = -1,
          length = array.length,
          first = array[0],
          mid = array[(length / 2) | 0],
          last = array[length - 1];
  
      if (first && typeof first == 'object' &&
          mid && typeof mid == 'object' && last && typeof last == 'object') {
        return false;
      }
      var cache = getObject();
      cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
  
      var result = getObject();
      result.array = array;
      result.cache = cache;
      result.push = cachePush;
  
      while (++index < length) {
        result.push(array[index]);
      }
      return result;
    }
  
    /**
     * Used by `template` to escape characters for inclusion in compiled
     * string literals.
     *
     * @private
     * @param {string} match The matched character to escape.
     * @returns {string} Returns the escaped character.
     */
    function escapeStringChar(match) {
      return '\\' + stringEscapes[match];
    }
  
    /**
     * Gets an array from the array pool or creates a new one if the pool is empty.
     *
     * @private
     * @returns {Array} The array from the pool.
     */
    function getArray() {
      return arrayPool.pop() || [];
    }
  
    /**
     * Gets an object from the object pool or creates a new one if the pool is empty.
     *
     * @private
     * @returns {Object} The object from the pool.
     */
    function getObject() {
      return objectPool.pop() || {
        'array': null,
        'cache': null,
        'criteria': null,
        'false': false,
        'index': 0,
        'null': false,
        'number': null,
        'object': null,
        'push': null,
        'string': null,
        'true': false,
        'undefined': false,
        'value': null
      };
    }
  
    /**
     * Releases the given array back to the array pool.
     *
     * @private
     * @param {Array} [array] The array to release.
     */
    function releaseArray(array) {
      array.length = 0;
      if (arrayPool.length < maxPoolSize) {
        arrayPool.push(array);
      }
    }
  
    /**
     * Releases the given object back to the object pool.
     *
     * @private
     * @param {Object} [object] The object to release.
     */
    function releaseObject(object) {
      var cache = object.cache;
      if (cache) {
        releaseObject(cache);
      }
      object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
      if (objectPool.length < maxPoolSize) {
        objectPool.push(object);
      }
    }
  
    /**
     * Slices the `collection` from the `start` index up to, but not including,
     * the `end` index.
     *
     * Note: This function is used instead of `Array#slice` to support node lists
     * in IE < 9 and to ensure dense arrays are returned.
     *
     * @private
     * @param {Array|Object|string} collection The collection to slice.
     * @param {number} start The start index.
     * @param {number} end The end index.
     * @returns {Array} Returns the new array.
     */
    function slice(array, start, end) {
      start || (start = 0);
      if (typeof end == 'undefined') {
        end = array ? array.length : 0;
      }
      var index = -1,
          length = end - start || 0,
          result = Array(length < 0 ? 0 : length);
  
      while (++index < length) {
        result[index] = array[start + index];
      }
      return result;
    }
  
    /*--------------------------------------------------------------------------*/
  
    /**
     * Create a new `lodash` function using the given context object.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {Object} [context=root] The context object.
     * @returns {Function} Returns the `lodash` function.
     */
    function runInContext(context) {
      // Avoid issues with some ES3 environments that attempt to use values, named
      // after built-in constructors like `Object`, for the creation of literals.
      // ES5 clears this up by stating that literals must use built-in constructors.
      // See http://es5.github.io/#x11.1.5.
      context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
  
      /** Native constructor references */
      var Array = context.Array,
          Boolean = context.Boolean,
          Date = context.Date,
          Function = context.Function,
          Math = context.Math,
          Number = context.Number,
          Object = context.Object,
          RegExp = context.RegExp,
          String = context.String,
          TypeError = context.TypeError;
  
      /**
       * Used for `Array` method references.
       *
       * Normally `Array.prototype` would suffice, however, using an array literal
       * avoids issues in Narwhal.
       */
      var arrayRef = [];
  
      /** Used for native method references */
      var objectProto = Object.prototype;
  
      /** Used to restore the original `_` reference in `noConflict` */
      var oldDash = context._;
  
      /** Used to resolve the internal [[Class]] of values */
      var toString = objectProto.toString;
  
      /** Used to detect if a method is native */
      var reNative = RegExp('^' +
        String(toString)
          .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
          .replace(/toString| for [^\]]+/g, '.*?') + '$'
      );
  
      /** Native method shortcuts */
      var ceil = Math.ceil,
          clearTimeout = context.clearTimeout,
          floor = Math.floor,
          fnToString = Function.prototype.toString,
          getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
          hasOwnProperty = objectProto.hasOwnProperty,
          push = arrayRef.push,
          setTimeout = context.setTimeout,
          splice = arrayRef.splice,
          unshift = arrayRef.unshift;
  
      /** Used to set meta data on functions */
      var defineProperty = (function() {
        // IE 8 only accepts DOM elements
        try {
          var o = {},
              func = isNative(func = Object.defineProperty) && func,
              result = func(o, o, o) && func;
        } catch(e) { }
        return result;
      }());
  
      /* Native method shortcuts for methods with the same name as other `lodash` methods */
      var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
          nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
          nativeIsFinite = context.isFinite,
          nativeIsNaN = context.isNaN,
          nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
          nativeMax = Math.max,
          nativeMin = Math.min,
          nativeParseInt = context.parseInt,
          nativeRandom = Math.random;
  
      /** Used to lookup a built-in constructor by [[Class]] */
      var ctorByClass = {};
      ctorByClass[arrayClass] = Array;
      ctorByClass[boolClass] = Boolean;
      ctorByClass[dateClass] = Date;
      ctorByClass[funcClass] = Function;
      ctorByClass[objectClass] = Object;
      ctorByClass[numberClass] = Number;
      ctorByClass[regexpClass] = RegExp;
      ctorByClass[stringClass] = String;
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates a `lodash` object which wraps the given value to enable intuitive
       * method chaining.
       *
       * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
       * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
       * and `unshift`
       *
       * Chaining is supported in custom builds as long as the `value` method is
       * implicitly or explicitly included in the build.
       *
       * The chainable wrapper functions are:
       * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
       * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
       * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
       * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
       * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
       * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
       * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
       * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
       * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
       * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
       * and `zip`
       *
       * The non-chainable wrapper functions are:
       * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
       * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
       * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
       * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
       * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
       * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
       * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
       * `template`, `unescape`, `uniqueId`, and `value`
       *
       * The wrapper functions `first` and `last` return wrapped values when `n` is
       * provided, otherwise they return unwrapped values.
       *
       * Explicit chaining can be enabled by using the `_.chain` method.
       *
       * @name _
       * @constructor
       * @category Chaining
       * @param {*} value The value to wrap in a `lodash` instance.
       * @returns {Object} Returns a `lodash` instance.
       * @example
       *
       * var wrapped = _([1, 2, 3]);
       *
       * // returns an unwrapped value
       * wrapped.reduce(function(sum, num) {
       *   return sum + num;
       * });
       * // => 6
       *
       * // returns a wrapped value
       * var squares = wrapped.map(function(num) {
       *   return num * num;
       * });
       *
       * _.isArray(squares);
       * // => false
       *
       * _.isArray(squares.value());
       * // => true
       */
      function lodash(value) {
        // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
        return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
         ? value
         : new lodashWrapper(value);
      }
  
      /**
       * A fast path for creating `lodash` wrapper objects.
       *
       * @private
       * @param {*} value The value to wrap in a `lodash` instance.
       * @param {boolean} chainAll A flag to enable chaining for all methods
       * @returns {Object} Returns a `lodash` instance.
       */
      function lodashWrapper(value, chainAll) {
        this.__chain__ = !!chainAll;
        this.__wrapped__ = value;
      }
      // ensure `new lodashWrapper` is an instance of `lodash`
      lodashWrapper.prototype = lodash.prototype;
  
      /**
       * An object used to flag environments features.
       *
       * @static
       * @memberOf _
       * @type Object
       */
      var support = lodash.support = {};
  
      /**
       * Detect if functions can be decompiled by `Function#toString`
       * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
       *
       * @memberOf _.support
       * @type boolean
       */
      support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
  
      /**
       * Detect if `Function#name` is supported (all but IE).
       *
       * @memberOf _.support
       * @type boolean
       */
      support.funcNames = typeof Function.name == 'string';
  
      /**
       * By default, the template delimiters used by Lo-Dash are similar to those in
       * embedded Ruby (ERB). Change the following template settings to use alternative
       * delimiters.
       *
       * @static
       * @memberOf _
       * @type Object
       */
      lodash.templateSettings = {
  
        /**
         * Used to detect `data` property values to be HTML-escaped.
         *
         * @memberOf _.templateSettings
         * @type RegExp
         */
        'escape': /<%-([\s\S]+?)%>/g,
  
        /**
         * Used to detect code to be evaluated.
         *
         * @memberOf _.templateSettings
         * @type RegExp
         */
        'evaluate': /<%([\s\S]+?)%>/g,
  
        /**
         * Used to detect `data` property values to inject.
         *
         * @memberOf _.templateSettings
         * @type RegExp
         */
        'interpolate': reInterpolate,
  
        /**
         * Used to reference the data object in the template text.
         *
         * @memberOf _.templateSettings
         * @type string
         */
        'variable': '',
  
        /**
         * Used to import variables into the compiled template.
         *
         * @memberOf _.templateSettings
         * @type Object
         */
        'imports': {
  
          /**
           * A reference to the `lodash` function.
           *
           * @memberOf _.templateSettings.imports
           * @type Function
           */
          '_': lodash
        }
      };
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * The base implementation of `_.bind` that creates the bound function and
       * sets its meta data.
       *
       * @private
       * @param {Array} bindData The bind data array.
       * @returns {Function} Returns the new bound function.
       */
      function baseBind(bindData) {
        var func = bindData[0],
            partialArgs = bindData[2],
            thisArg = bindData[4];
  
        function bound() {
          // `Function#bind` spec
          // http://es5.github.io/#x15.3.4.5
          if (partialArgs) {
            // avoid `arguments` object deoptimizations by using `slice` instead
            // of `Array.prototype.slice.call` and not assigning `arguments` to a
            // variable as a ternary expression
            var args = slice(partialArgs);
            push.apply(args, arguments);
          }
          // mimic the constructor's `return` behavior
          // http://es5.github.io/#x13.2.2
          if (this instanceof bound) {
            // ensure `new bound` is an instance of `func`
            var thisBinding = baseCreate(func.prototype),
                result = func.apply(thisBinding, args || arguments);
            return isObject(result) ? result : thisBinding;
          }
          return func.apply(thisArg, args || arguments);
        }
        setBindData(bound, bindData);
        return bound;
      }
  
      /**
       * The base implementation of `_.clone` without argument juggling or support
       * for `thisArg` binding.
       *
       * @private
       * @param {*} value The value to clone.
       * @param {boolean} [isDeep=false] Specify a deep clone.
       * @param {Function} [callback] The function to customize cloning values.
       * @param {Array} [stackA=[]] Tracks traversed source objects.
       * @param {Array} [stackB=[]] Associates clones with source counterparts.
       * @returns {*} Returns the cloned value.
       */
      function baseClone(value, isDeep, callback, stackA, stackB) {
        if (callback) {
          var result = callback(value);
          if (typeof result != 'undefined') {
            return result;
          }
        }
        // inspect [[Class]]
        var isObj = isObject(value);
        if (isObj) {
          var className = toString.call(value);
          if (!cloneableClasses[className]) {
            return value;
          }
          var ctor = ctorByClass[className];
          switch (className) {
            case boolClass:
            case dateClass:
              return new ctor(+value);
  
            case numberClass:
            case stringClass:
              return new ctor(value);
  
            case regexpClass:
              result = ctor(value.source, reFlags.exec(value));
              result.lastIndex = value.lastIndex;
              return result;
          }
        } else {
          return value;
        }
        var isArr = isArray(value);
        if (isDeep) {
          // check for circular references and return corresponding clone
          var initedStack = !stackA;
          stackA || (stackA = getArray());
          stackB || (stackB = getArray());
  
          var length = stackA.length;
          while (length--) {
            if (stackA[length] == value) {
              return stackB[length];
            }
          }
          result = isArr ? ctor(value.length) : {};
        }
        else {
          result = isArr ? slice(value) : assign({}, value);
        }
        // add array properties assigned by `RegExp#exec`
        if (isArr) {
          if (hasOwnProperty.call(value, 'index')) {
            result.index = value.index;
          }
          if (hasOwnProperty.call(value, 'input')) {
            result.input = value.input;
          }
        }
        // exit for shallow clone
        if (!isDeep) {
          return result;
        }
        // add the source value to the stack of traversed objects
        // and associate it with its clone
        stackA.push(value);
        stackB.push(result);
  
        // recursively populate clone (susceptible to call stack limits)
        (isArr ? forEach : forOwn)(value, function(objValue, key) {
          result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
        });
  
        if (initedStack) {
          releaseArray(stackA);
          releaseArray(stackB);
        }
        return result;
      }
  
      /**
       * The base implementation of `_.create` without support for assigning
       * properties to the created object.
       *
       * @private
       * @param {Object} prototype The object to inherit from.
       * @returns {Object} Returns the new object.
       */
      function baseCreate(prototype, properties) {
        return isObject(prototype) ? nativeCreate(prototype) : {};
      }
      // fallback for browsers without `Object.create`
      if (!nativeCreate) {
        baseCreate = (function() {
          function Object() {}
          return function(prototype) {
            if (isObject(prototype)) {
              Object.prototype = prototype;
              var result = new Object;
              Object.prototype = null;
            }
            return result || context.Object();
          };
        }());
      }
  
      /**
       * The base implementation of `_.createCallback` without support for creating
       * "_.pluck" or "_.where" style callbacks.
       *
       * @private
       * @param {*} [func=identity] The value to convert to a callback.
       * @param {*} [thisArg] The `this` binding of the created callback.
       * @param {number} [argCount] The number of arguments the callback accepts.
       * @returns {Function} Returns a callback function.
       */
      function baseCreateCallback(func, thisArg, argCount) {
        if (typeof func != 'function') {
          return identity;
        }
        // exit early for no `thisArg` or already bound by `Function#bind`
        if (typeof thisArg == 'undefined' || !('prototype' in func)) {
          return func;
        }
        var bindData = func.__bindData__;
        if (typeof bindData == 'undefined') {
          if (support.funcNames) {
            bindData = !func.name;
          }
          bindData = bindData || !support.funcDecomp;
          if (!bindData) {
            var source = fnToString.call(func);
            if (!support.funcNames) {
              bindData = !reFuncName.test(source);
            }
            if (!bindData) {
              // checks if `func` references the `this` keyword and stores the result
              bindData = reThis.test(source);
              setBindData(func, bindData);
            }
          }
        }
        // exit early if there are no `this` references or `func` is bound
        if (bindData === false || (bindData !== true && bindData[1] & 1)) {
          return func;
        }
        switch (argCount) {
          case 1: return function(value) {
            return func.call(thisArg, value);
          };
          case 2: return function(a, b) {
            return func.call(thisArg, a, b);
          };
          case 3: return function(value, index, collection) {
            return func.call(thisArg, value, index, collection);
          };
          case 4: return function(accumulator, value, index, collection) {
            return func.call(thisArg, accumulator, value, index, collection);
          };
        }
        return bind(func, thisArg);
      }
  
      /**
       * The base implementation of `createWrapper` that creates the wrapper and
       * sets its meta data.
       *
       * @private
       * @param {Array} bindData The bind data array.
       * @returns {Function} Returns the new function.
       */
      function baseCreateWrapper(bindData) {
        var func = bindData[0],
            bitmask = bindData[1],
            partialArgs = bindData[2],
            partialRightArgs = bindData[3],
            thisArg = bindData[4],
            arity = bindData[5];
  
        var isBind = bitmask & 1,
            isBindKey = bitmask & 2,
            isCurry = bitmask & 4,
            isCurryBound = bitmask & 8,
            key = func;
  
        function bound() {
          var thisBinding = isBind ? thisArg : this;
          if (partialArgs) {
            var args = slice(partialArgs);
            push.apply(args, arguments);
          }
          if (partialRightArgs || isCurry) {
            args || (args = slice(arguments));
            if (partialRightArgs) {
              push.apply(args, partialRightArgs);
            }
            if (isCurry && args.length < arity) {
              bitmask |= 16 & ~32;
              return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
            }
          }
          args || (args = arguments);
          if (isBindKey) {
            func = thisBinding[key];
          }
          if (this instanceof bound) {
            thisBinding = baseCreate(func.prototype);
            var result = func.apply(thisBinding, args);
            return isObject(result) ? result : thisBinding;
          }
          return func.apply(thisBinding, args);
        }
        setBindData(bound, bindData);
        return bound;
      }
  
      /**
       * The base implementation of `_.difference` that accepts a single array
       * of values to exclude.
       *
       * @private
       * @param {Array} array The array to process.
       * @param {Array} [values] The array of values to exclude.
       * @returns {Array} Returns a new array of filtered values.
       */
      function baseDifference(array, values) {
        var index = -1,
            indexOf = getIndexOf(),
            length = array ? array.length : 0,
            isLarge = length >= largeArraySize && indexOf === baseIndexOf,
            result = [];
  
        if (isLarge) {
          var cache = createCache(values);
          if (cache) {
            indexOf = cacheIndexOf;
            values = cache;
          } else {
            isLarge = false;
          }
        }
        while (++index < length) {
          var value = array[index];
          if (indexOf(values, value) < 0) {
            result.push(value);
          }
        }
        if (isLarge) {
          releaseObject(values);
        }
        return result;
      }
  
      /**
       * The base implementation of `_.flatten` without support for callback
       * shorthands or `thisArg` binding.
       *
       * @private
       * @param {Array} array The array to flatten.
       * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
       * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
       * @param {number} [fromIndex=0] The index to start from.
       * @returns {Array} Returns a new flattened array.
       */
      function baseFlatten(array, isShallow, isStrict, fromIndex) {
        var index = (fromIndex || 0) - 1,
            length = array ? array.length : 0,
            result = [];
  
        while (++index < length) {
          var value = array[index];
  
          if (value && typeof value == 'object' && typeof value.length == 'number'
              && (isArray(value) || isArguments(value))) {
            // recursively flatten arrays (susceptible to call stack limits)
            if (!isShallow) {
              value = baseFlatten(value, isShallow, isStrict);
            }
            var valIndex = -1,
                valLength = value.length,
                resIndex = result.length;
  
            result.length += valLength;
            while (++valIndex < valLength) {
              result[resIndex++] = value[valIndex];
            }
          } else if (!isStrict) {
            result.push(value);
          }
        }
        return result;
      }
  
      /**
       * The base implementation of `_.isEqual`, without support for `thisArg` binding,
       * that allows partial "_.where" style comparisons.
       *
       * @private
       * @param {*} a The value to compare.
       * @param {*} b The other value to compare.
       * @param {Function} [callback] The function to customize comparing values.
       * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
       * @param {Array} [stackA=[]] Tracks traversed `a` objects.
       * @param {Array} [stackB=[]] Tracks traversed `b` objects.
       * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
       */
      function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
        // used to indicate that when comparing objects, `a` has at least the properties of `b`
        if (callback) {
          var result = callback(a, b);
          if (typeof result != 'undefined') {
            return !!result;
          }
        }
        // exit early for identical values
        if (a === b) {
          // treat `+0` vs. `-0` as not equal
          return a !== 0 || (1 / a == 1 / b);
        }
        var type = typeof a,
            otherType = typeof b;
  
        // exit early for unlike primitive values
        if (a === a &&
            !(a && objectTypes[type]) &&
            !(b && objectTypes[otherType])) {
          return false;
        }
        // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
        // http://es5.github.io/#x15.3.4.4
        if (a == null || b == null) {
          return a === b;
        }
        // compare [[Class]] names
        var className = toString.call(a),
            otherClass = toString.call(b);
  
        if (className == argsClass) {
          className = objectClass;
        }
        if (otherClass == argsClass) {
          otherClass = objectClass;
        }
        if (className != otherClass) {
          return false;
        }
        switch (className) {
          case boolClass:
          case dateClass:
            // coerce dates and booleans to numbers, dates to milliseconds and booleans
            // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
            return +a == +b;
  
          case numberClass:
            // treat `NaN` vs. `NaN` as equal
            return (a != +a)
              ? b != +b
              // but treat `+0` vs. `-0` as not equal
              : (a == 0 ? (1 / a == 1 / b) : a == +b);
  
          case regexpClass:
          case stringClass:
            // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
            // treat string primitives and their corresponding object instances as equal
            return a == String(b);
        }
        var isArr = className == arrayClass;
        if (!isArr) {
          // unwrap any `lodash` wrapped values
          var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
              bWrapped = hasOwnProperty.call(b, '__wrapped__');
  
          if (aWrapped || bWrapped) {
            return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
          }
          // exit for functions and DOM nodes
          if (className != objectClass) {
            return false;
          }
          // in older versions of Opera, `arguments` objects have `Array` constructors
          var ctorA = a.constructor,
              ctorB = b.constructor;
  
          // non `Object` object instances with different constructors are not equal
          if (ctorA != ctorB &&
                !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
                ('constructor' in a && 'constructor' in b)
              ) {
            return false;
          }
        }
        // assume cyclic structures are equal
        // the algorithm for detecting cyclic structures is adapted from ES 5.1
        // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
        var initedStack = !stackA;
        stackA || (stackA = getArray());
        stackB || (stackB = getArray());
  
        var length = stackA.length;
        while (length--) {
          if (stackA[length] == a) {
            return stackB[length] == b;
          }
        }
        var size = 0;
        result = true;
  
        // add `a` and `b` to the stack of traversed objects
        stackA.push(a);
        stackB.push(b);
  
        // recursively compare objects and arrays (susceptible to call stack limits)
        if (isArr) {
          // compare lengths to determine if a deep comparison is necessary
          length = a.length;
          size = b.length;
          result = size == length;
  
          if (result || isWhere) {
            // deep compare the contents, ignoring non-numeric properties
            while (size--) {
              var index = length,
                  value = b[size];
  
              if (isWhere) {
                while (index--) {
                  if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
                    break;
                  }
                }
              } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
                break;
              }
            }
          }
        }
        else {
          // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
          // which, in this case, is more costly
          forIn(b, function(value, key, b) {
            if (hasOwnProperty.call(b, key)) {
              // count the number of properties.
              size++;
              // deep compare each property value.
              return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
            }
          });
  
          if (result && !isWhere) {
            // ensure both objects have the same number of properties
            forIn(a, function(value, key, a) {
              if (hasOwnProperty.call(a, key)) {
                // `size` will be `-1` if `a` has more properties than `b`
                return (result = --size > -1);
              }
            });
          }
        }
        stackA.pop();
        stackB.pop();
  
        if (initedStack) {
          releaseArray(stackA);
          releaseArray(stackB);
        }
        return result;
      }
  
      /**
       * The base implementation of `_.merge` without argument juggling or support
       * for `thisArg` binding.
       *
       * @private
       * @param {Object} object The destination object.
       * @param {Object} source The source object.
       * @param {Function} [callback] The function to customize merging properties.
       * @param {Array} [stackA=[]] Tracks traversed source objects.
       * @param {Array} [stackB=[]] Associates values with source counterparts.
       */
      function baseMerge(object, source, callback, stackA, stackB) {
        (isArray(source) ? forEach : forOwn)(source, function(source, key) {
          var found,
              isArr,
              result = source,
              value = object[key];
  
          if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
            // avoid merging previously merged cyclic sources
            var stackLength = stackA.length;
            while (stackLength--) {
              if ((found = stackA[stackLength] == source)) {
                value = stackB[stackLength];
                break;
              }
            }
            if (!found) {
              var isShallow;
              if (callback) {
                result = callback(value, source);
                if ((isShallow = typeof result != 'undefined')) {
                  value = result;
                }
              }
              if (!isShallow) {
                value = isArr
                  ? (isArray(value) ? value : [])
                  : (isPlainObject(value) ? value : {});
              }
              // add `source` and associated `value` to the stack of traversed objects
              stackA.push(source);
              stackB.push(value);
  
              // recursively merge objects and arrays (susceptible to call stack limits)
              if (!isShallow) {
                baseMerge(value, source, callback, stackA, stackB);
              }
            }
          }
          else {
            if (callback) {
              result = callback(value, source);
              if (typeof result == 'undefined') {
                result = source;
              }
            }
            if (typeof result != 'undefined') {
              value = result;
            }
          }
          object[key] = value;
        });
      }
  
      /**
       * The base implementation of `_.random` without argument juggling or support
       * for returning floating-point numbers.
       *
       * @private
       * @param {number} min The minimum possible value.
       * @param {number} max The maximum possible value.
       * @returns {number} Returns a random number.
       */
      function baseRandom(min, max) {
        return min + floor(nativeRandom() * (max - min + 1));
      }
  
      /**
       * The base implementation of `_.uniq` without support for callback shorthands
       * or `thisArg` binding.
       *
       * @private
       * @param {Array} array The array to process.
       * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
       * @param {Function} [callback] The function called per iteration.
       * @returns {Array} Returns a duplicate-value-free array.
       */
      function baseUniq(array, isSorted, callback) {
        var index = -1,
            indexOf = getIndexOf(),
            length = array ? array.length : 0,
            result = [];
  
        var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
            seen = (callback || isLarge) ? getArray() : result;
  
        if (isLarge) {
          var cache = createCache(seen);
          indexOf = cacheIndexOf;
          seen = cache;
        }
        while (++index < length) {
          var value = array[index],
              computed = callback ? callback(value, index, array) : value;
  
          if (isSorted
                ? !index || seen[seen.length - 1] !== computed
                : indexOf(seen, computed) < 0
              ) {
            if (callback || isLarge) {
              seen.push(computed);
            }
            result.push(value);
          }
        }
        if (isLarge) {
          releaseArray(seen.array);
          releaseObject(seen);
        } else if (callback) {
          releaseArray(seen);
        }
        return result;
      }
  
      /**
       * Creates a function that aggregates a collection, creating an object composed
       * of keys generated from the results of running each element of the collection
       * through a callback. The given `setter` function sets the keys and values
       * of the composed object.
       *
       * @private
       * @param {Function} setter The setter function.
       * @returns {Function} Returns the new aggregator function.
       */
      function createAggregator(setter) {
        return function(collection, callback, thisArg) {
          var result = {};
          callback = lodash.createCallback(callback, thisArg, 3);
  
          var index = -1,
              length = collection ? collection.length : 0;
  
          if (typeof length == 'number') {
            while (++index < length) {
              var value = collection[index];
              setter(result, value, callback(value, index, collection), collection);
            }
          } else {
            forOwn(collection, function(value, key, collection) {
              setter(result, value, callback(value, key, collection), collection);
            });
          }
          return result;
        };
      }
  
      /**
       * Creates a function that, when called, either curries or invokes `func`
       * with an optional `this` binding and partially applied arguments.
       *
       * @private
       * @param {Function|string} func The function or method name to reference.
       * @param {number} bitmask The bitmask of method flags to compose.
       *  The bitmask may be composed of the following flags:
       *  1 - `_.bind`
       *  2 - `_.bindKey`
       *  4 - `_.curry`
       *  8 - `_.curry` (bound)
       *  16 - `_.partial`
       *  32 - `_.partialRight`
       * @param {Array} [partialArgs] An array of arguments to prepend to those
       *  provided to the new function.
       * @param {Array} [partialRightArgs] An array of arguments to append to those
       *  provided to the new function.
       * @param {*} [thisArg] The `this` binding of `func`.
       * @param {number} [arity] The arity of `func`.
       * @returns {Function} Returns the new function.
       */
      function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
        var isBind = bitmask & 1,
            isBindKey = bitmask & 2,
            isCurry = bitmask & 4,
            isCurryBound = bitmask & 8,
            isPartial = bitmask & 16,
            isPartialRight = bitmask & 32;
  
        if (!isBindKey && !isFunction(func)) {
          throw new TypeError;
        }
        if (isPartial && !partialArgs.length) {
          bitmask &= ~16;
          isPartial = partialArgs = false;
        }
        if (isPartialRight && !partialRightArgs.length) {
          bitmask &= ~32;
          isPartialRight = partialRightArgs = false;
        }
        var bindData = func && func.__bindData__;
        if (bindData && bindData !== true) {
          // clone `bindData`
          bindData = slice(bindData);
          if (bindData[2]) {
            bindData[2] = slice(bindData[2]);
          }
          if (bindData[3]) {
            bindData[3] = slice(bindData[3]);
          }
          // set `thisBinding` is not previously bound
          if (isBind && !(bindData[1] & 1)) {
            bindData[4] = thisArg;
          }
          // set if previously bound but not currently (subsequent curried functions)
          if (!isBind && bindData[1] & 1) {
            bitmask |= 8;
          }
          // set curried arity if not yet set
          if (isCurry && !(bindData[1] & 4)) {
            bindData[5] = arity;
          }
          // append partial left arguments
          if (isPartial) {
            push.apply(bindData[2] || (bindData[2] = []), partialArgs);
          }
          // append partial right arguments
          if (isPartialRight) {
            unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
          }
          // merge flags
          bindData[1] |= bitmask;
          return createWrapper.apply(null, bindData);
        }
        // fast path for `_.bind`
        var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
        return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
      }
  
      /**
       * Used by `escape` to convert characters to HTML entities.
       *
       * @private
       * @param {string} match The matched character to escape.
       * @returns {string} Returns the escaped character.
       */
      function escapeHtmlChar(match) {
        return htmlEscapes[match];
      }
  
      /**
       * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
       * customized, this method returns the custom method, otherwise it returns
       * the `baseIndexOf` function.
       *
       * @private
       * @returns {Function} Returns the "indexOf" function.
       */
      function getIndexOf() {
        var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
        return result;
      }
  
      /**
       * Checks if `value` is a native function.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
       */
      function isNative(value) {
        return typeof value == 'function' && reNative.test(value);
      }
  
      /**
       * Sets `this` binding data on a given function.
       *
       * @private
       * @param {Function} func The function to set data on.
       * @param {Array} value The data array to set.
       */
      var setBindData = !defineProperty ? noop : function(func, value) {
        descriptor.value = value;
        defineProperty(func, '__bindData__', descriptor);
      };
  
      /**
       * A fallback implementation of `isPlainObject` which checks if a given value
       * is an object created by the `Object` constructor, assuming objects created
       * by the `Object` constructor have no inherited enumerable properties and that
       * there are no `Object.prototype` extensions.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
       */
      function shimIsPlainObject(value) {
        var ctor,
            result;
  
        // avoid non Object objects, `arguments` objects, and DOM elements
        if (!(value && toString.call(value) == objectClass) ||
            (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
          return false;
        }
        // In most environments an object's own properties are iterated before
        // its inherited properties. If the last iterated property is an object's
        // own property then there are no inherited enumerable properties.
        forIn(value, function(value, key) {
          result = key;
        });
        return typeof result == 'undefined' || hasOwnProperty.call(value, result);
      }
  
      /**
       * Used by `unescape` to convert HTML entities to characters.
       *
       * @private
       * @param {string} match The matched character to unescape.
       * @returns {string} Returns the unescaped character.
       */
      function unescapeHtmlChar(match) {
        return htmlUnescapes[match];
      }
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Checks if `value` is an `arguments` object.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
       * @example
       *
       * (function() { return _.isArguments(arguments); })(1, 2, 3);
       * // => true
       *
       * _.isArguments([1, 2, 3]);
       * // => false
       */
      function isArguments(value) {
        return value && typeof value == 'object' && typeof value.length == 'number' &&
          toString.call(value) == argsClass || false;
      }
  
      /**
       * Checks if `value` is an array.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
       * @example
       *
       * (function() { return _.isArray(arguments); })();
       * // => false
       *
       * _.isArray([1, 2, 3]);
       * // => true
       */
      var isArray = nativeIsArray || function(value) {
        return value && typeof value == 'object' && typeof value.length == 'number' &&
          toString.call(value) == arrayClass || false;
      };
  
      /**
       * A fallback implementation of `Object.keys` which produces an array of the
       * given object's own enumerable property names.
       *
       * @private
       * @type Function
       * @param {Object} object The object to inspect.
       * @returns {Array} Returns an array of property names.
       */
      var shimKeys = function(object) {
        var index, iterable = object, result = [];
        if (!iterable) return result;
        if (!(objectTypes[typeof object])) return result;
          for (index in iterable) {
            if (hasOwnProperty.call(iterable, index)) {
              result.push(index);
            }
          }
        return result
      };
  
      /**
       * Creates an array composed of the own enumerable property names of an object.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to inspect.
       * @returns {Array} Returns an array of property names.
       * @example
       *
       * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
       * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
       */
      var keys = !nativeKeys ? shimKeys : function(object) {
        if (!isObject(object)) {
          return [];
        }
        return nativeKeys(object);
      };
  
      /**
       * Used to convert characters to HTML entities:
       *
       * Though the `>` character is escaped for symmetry, characters like `>` and `/`
       * don't require escaping in HTML and have no special meaning unless they're part
       * of a tag or an unquoted attribute value.
       * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
       */
      var htmlEscapes = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#39;'
      };
  
      /** Used to convert HTML entities to characters */
      var htmlUnescapes = invert(htmlEscapes);
  
      /** Used to match HTML entities and HTML characters */
      var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
          reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Assigns own enumerable properties of source object(s) to the destination
       * object. Subsequent sources will overwrite property assignments of previous
       * sources. If a callback is provided it will be executed to produce the
       * assigned values. The callback is bound to `thisArg` and invoked with two
       * arguments; (objectValue, sourceValue).
       *
       * @static
       * @memberOf _
       * @type Function
       * @alias extend
       * @category Objects
       * @param {Object} object The destination object.
       * @param {...Object} [source] The source objects.
       * @param {Function} [callback] The function to customize assigning values.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns the destination object.
       * @example
       *
       * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
       * // => { 'name': 'fred', 'employer': 'slate' }
       *
       * var defaults = _.partialRight(_.assign, function(a, b) {
       *   return typeof a == 'undefined' ? b : a;
       * });
       *
       * var object = { 'name': 'barney' };
       * defaults(object, { 'name': 'fred', 'employer': 'slate' });
       * // => { 'name': 'barney', 'employer': 'slate' }
       */
      var assign = function(object, source, guard) {
        var index, iterable = object, result = iterable;
        if (!iterable) return result;
        var args = arguments,
            argsIndex = 0,
            argsLength = typeof guard == 'number' ? 2 : args.length;
        if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
          var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
        } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
          callback = args[--argsLength];
        }
        while (++argsIndex < argsLength) {
          iterable = args[argsIndex];
          if (iterable && objectTypes[typeof iterable]) {
          var ownIndex = -1,
              ownProps = objectTypes[typeof iterable] && keys(iterable),
              length = ownProps ? ownProps.length : 0;
  
          while (++ownIndex < length) {
            index = ownProps[ownIndex];
            result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
          }
          }
        }
        return result
      };
  
      /**
       * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
       * be cloned, otherwise they will be assigned by reference. If a callback
       * is provided it will be executed to produce the cloned values. If the
       * callback returns `undefined` cloning will be handled by the method instead.
       * The callback is bound to `thisArg` and invoked with one argument; (value).
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to clone.
       * @param {boolean} [isDeep=false] Specify a deep clone.
       * @param {Function} [callback] The function to customize cloning values.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the cloned value.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * var shallow = _.clone(characters);
       * shallow[0] === characters[0];
       * // => true
       *
       * var deep = _.clone(characters, true);
       * deep[0] === characters[0];
       * // => false
       *
       * _.mixin({
       *   'clone': _.partialRight(_.clone, function(value) {
       *     return _.isElement(value) ? value.cloneNode(false) : undefined;
       *   })
       * });
       *
       * var clone = _.clone(document.body);
       * clone.childNodes.length;
       * // => 0
       */
      function clone(value, isDeep, callback, thisArg) {
        // allows working with "Collections" methods without using their `index`
        // and `collection` arguments for `isDeep` and `callback`
        if (typeof isDeep != 'boolean' && isDeep != null) {
          thisArg = callback;
          callback = isDeep;
          isDeep = false;
        }
        return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
      }
  
      /**
       * Creates a deep clone of `value`. If a callback is provided it will be
       * executed to produce the cloned values. If the callback returns `undefined`
       * cloning will be handled by the method instead. The callback is bound to
       * `thisArg` and invoked with one argument; (value).
       *
       * Note: This method is loosely based on the structured clone algorithm. Functions
       * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
       * objects created by constructors other than `Object` are cloned to plain `Object` objects.
       * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to deep clone.
       * @param {Function} [callback] The function to customize cloning values.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the deep cloned value.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * var deep = _.cloneDeep(characters);
       * deep[0] === characters[0];
       * // => false
       *
       * var view = {
       *   'label': 'docs',
       *   'node': element
       * };
       *
       * var clone = _.cloneDeep(view, function(value) {
       *   return _.isElement(value) ? value.cloneNode(true) : undefined;
       * });
       *
       * clone.node == view.node;
       * // => false
       */
      function cloneDeep(value, callback, thisArg) {
        return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
      }
  
      /**
       * Creates an object that inherits from the given `prototype` object. If a
       * `properties` object is provided its own enumerable properties are assigned
       * to the created object.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} prototype The object to inherit from.
       * @param {Object} [properties] The properties to assign to the object.
       * @returns {Object} Returns the new object.
       * @example
       *
       * function Shape() {
       *   this.x = 0;
       *   this.y = 0;
       * }
       *
       * function Circle() {
       *   Shape.call(this);
       * }
       *
       * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
       *
       * var circle = new Circle;
       * circle instanceof Circle;
       * // => true
       *
       * circle instanceof Shape;
       * // => true
       */
      function create(prototype, properties) {
        var result = baseCreate(prototype);
        return properties ? assign(result, properties) : result;
      }
  
      /**
       * Assigns own enumerable properties of source object(s) to the destination
       * object for all destination properties that resolve to `undefined`. Once a
       * property is set, additional defaults of the same property will be ignored.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Objects
       * @param {Object} object The destination object.
       * @param {...Object} [source] The source objects.
       * @param- {Object} [guard] Allows working with `_.reduce` without using its
       *  `key` and `object` arguments as sources.
       * @returns {Object} Returns the destination object.
       * @example
       *
       * var object = { 'name': 'barney' };
       * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
       * // => { 'name': 'barney', 'employer': 'slate' }
       */
      var defaults = function(object, source, guard) {
        var index, iterable = object, result = iterable;
        if (!iterable) return result;
        var args = arguments,
            argsIndex = 0,
            argsLength = typeof guard == 'number' ? 2 : args.length;
        while (++argsIndex < argsLength) {
          iterable = args[argsIndex];
          if (iterable && objectTypes[typeof iterable]) {
          var ownIndex = -1,
              ownProps = objectTypes[typeof iterable] && keys(iterable),
              length = ownProps ? ownProps.length : 0;
  
          while (++ownIndex < length) {
            index = ownProps[ownIndex];
            if (typeof result[index] == 'undefined') result[index] = iterable[index];
          }
          }
        }
        return result
      };
  
      /**
       * This method is like `_.findIndex` except that it returns the key of the
       * first element that passes the callback check, instead of the element itself.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to search.
       * @param {Function|Object|string} [callback=identity] The function called per
       *  iteration. If a property name or object is provided it will be used to
       *  create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {string|undefined} Returns the key of the found element, else `undefined`.
       * @example
       *
       * var characters = {
       *   'barney': {  'age': 36, 'blocked': false },
       *   'fred': {    'age': 40, 'blocked': true },
       *   'pebbles': { 'age': 1,  'blocked': false }
       * };
       *
       * _.findKey(characters, function(chr) {
       *   return chr.age < 40;
       * });
       * // => 'barney' (property order is not guaranteed across environments)
       *
       * // using "_.where" callback shorthand
       * _.findKey(characters, { 'age': 1 });
       * // => 'pebbles'
       *
       * // using "_.pluck" callback shorthand
       * _.findKey(characters, 'blocked');
       * // => 'fred'
       */
      function findKey(object, callback, thisArg) {
        var result;
        callback = lodash.createCallback(callback, thisArg, 3);
        forOwn(object, function(value, key, object) {
          if (callback(value, key, object)) {
            result = key;
            return false;
          }
        });
        return result;
      }
  
      /**
       * This method is like `_.findKey` except that it iterates over elements
       * of a `collection` in the opposite order.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to search.
       * @param {Function|Object|string} [callback=identity] The function called per
       *  iteration. If a property name or object is provided it will be used to
       *  create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {string|undefined} Returns the key of the found element, else `undefined`.
       * @example
       *
       * var characters = {
       *   'barney': {  'age': 36, 'blocked': true },
       *   'fred': {    'age': 40, 'blocked': false },
       *   'pebbles': { 'age': 1,  'blocked': true }
       * };
       *
       * _.findLastKey(characters, function(chr) {
       *   return chr.age < 40;
       * });
       * // => returns `pebbles`, assuming `_.findKey` returns `barney`
       *
       * // using "_.where" callback shorthand
       * _.findLastKey(characters, { 'age': 40 });
       * // => 'fred'
       *
       * // using "_.pluck" callback shorthand
       * _.findLastKey(characters, 'blocked');
       * // => 'pebbles'
       */
      function findLastKey(object, callback, thisArg) {
        var result;
        callback = lodash.createCallback(callback, thisArg, 3);
        forOwnRight(object, function(value, key, object) {
          if (callback(value, key, object)) {
            result = key;
            return false;
          }
        });
        return result;
      }
  
      /**
       * Iterates over own and inherited enumerable properties of an object,
       * executing the callback for each property. The callback is bound to `thisArg`
       * and invoked with three arguments; (value, key, object). Callbacks may exit
       * iteration early by explicitly returning `false`.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Objects
       * @param {Object} object The object to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns `object`.
       * @example
       *
       * function Shape() {
       *   this.x = 0;
       *   this.y = 0;
       * }
       *
       * Shape.prototype.move = function(x, y) {
       *   this.x += x;
       *   this.y += y;
       * };
       *
       * _.forIn(new Shape, function(value, key) {
       *   console.log(key);
       * });
       * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
       */
      var forIn = function(collection, callback, thisArg) {
        var index, iterable = collection, result = iterable;
        if (!iterable) return result;
        if (!objectTypes[typeof iterable]) return result;
        callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
          for (index in iterable) {
            if (callback(iterable[index], index, collection) === false) return result;
          }
        return result
      };
  
      /**
       * This method is like `_.forIn` except that it iterates over elements
       * of a `collection` in the opposite order.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns `object`.
       * @example
       *
       * function Shape() {
       *   this.x = 0;
       *   this.y = 0;
       * }
       *
       * Shape.prototype.move = function(x, y) {
       *   this.x += x;
       *   this.y += y;
       * };
       *
       * _.forInRight(new Shape, function(value, key) {
       *   console.log(key);
       * });
       * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
       */
      function forInRight(object, callback, thisArg) {
        var pairs = [];
  
        forIn(object, function(value, key) {
          pairs.push(key, value);
        });
  
        var length = pairs.length;
        callback = baseCreateCallback(callback, thisArg, 3);
        while (length--) {
          if (callback(pairs[length--], pairs[length], object) === false) {
            break;
          }
        }
        return object;
      }
  
      /**
       * Iterates over own enumerable properties of an object, executing the callback
       * for each property. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, key, object). Callbacks may exit iteration early by
       * explicitly returning `false`.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Objects
       * @param {Object} object The object to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns `object`.
       * @example
       *
       * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
       *   console.log(key);
       * });
       * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
       */
      var forOwn = function(collection, callback, thisArg) {
        var index, iterable = collection, result = iterable;
        if (!iterable) return result;
        if (!objectTypes[typeof iterable]) return result;
        callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
          var ownIndex = -1,
              ownProps = objectTypes[typeof iterable] && keys(iterable),
              length = ownProps ? ownProps.length : 0;
  
          while (++ownIndex < length) {
            index = ownProps[ownIndex];
            if (callback(iterable[index], index, collection) === false) return result;
          }
        return result
      };
  
      /**
       * This method is like `_.forOwn` except that it iterates over elements
       * of a `collection` in the opposite order.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns `object`.
       * @example
       *
       * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
       *   console.log(key);
       * });
       * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
       */
      function forOwnRight(object, callback, thisArg) {
        var props = keys(object),
            length = props.length;
  
        callback = baseCreateCallback(callback, thisArg, 3);
        while (length--) {
          var key = props[length];
          if (callback(object[key], key, object) === false) {
            break;
          }
        }
        return object;
      }
  
      /**
       * Creates a sorted array of property names of all enumerable properties,
       * own and inherited, of `object` that have function values.
       *
       * @static
       * @memberOf _
       * @alias methods
       * @category Objects
       * @param {Object} object The object to inspect.
       * @returns {Array} Returns an array of property names that have function values.
       * @example
       *
       * _.functions(_);
       * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
       */
      function functions(object) {
        var result = [];
        forIn(object, function(value, key) {
          if (isFunction(value)) {
            result.push(key);
          }
        });
        return result.sort();
      }
  
      /**
       * Checks if the specified property name exists as a direct property of `object`,
       * instead of an inherited property.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to inspect.
       * @param {string} key The name of the property to check.
       * @returns {boolean} Returns `true` if key is a direct property, else `false`.
       * @example
       *
       * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
       * // => true
       */
      function has(object, key) {
        return object ? hasOwnProperty.call(object, key) : false;
      }
  
      /**
       * Creates an object composed of the inverted keys and values of the given object.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to invert.
       * @returns {Object} Returns the created inverted object.
       * @example
       *
       * _.invert({ 'first': 'fred', 'second': 'barney' });
       * // => { 'fred': 'first', 'barney': 'second' }
       */
      function invert(object) {
        var index = -1,
            props = keys(object),
            length = props.length,
            result = {};
  
        while (++index < length) {
          var key = props[index];
          result[object[key]] = key;
        }
        return result;
      }
  
      /**
       * Checks if `value` is a boolean value.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
       * @example
       *
       * _.isBoolean(null);
       * // => false
       */
      function isBoolean(value) {
        return value === true || value === false ||
          value && typeof value == 'object' && toString.call(value) == boolClass || false;
      }
  
      /**
       * Checks if `value` is a date.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
       * @example
       *
       * _.isDate(new Date);
       * // => true
       */
      function isDate(value) {
        return value && typeof value == 'object' && toString.call(value) == dateClass || false;
      }
  
      /**
       * Checks if `value` is a DOM element.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
       * @example
       *
       * _.isElement(document.body);
       * // => true
       */
      function isElement(value) {
        return value && value.nodeType === 1 || false;
      }
  
      /**
       * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
       * length of `0` and objects with no own enumerable properties are considered
       * "empty".
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Array|Object|string} value The value to inspect.
       * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
       * @example
       *
       * _.isEmpty([1, 2, 3]);
       * // => false
       *
       * _.isEmpty({});
       * // => true
       *
       * _.isEmpty('');
       * // => true
       */
      function isEmpty(value) {
        var result = true;
        if (!value) {
          return result;
        }
        var className = toString.call(value),
            length = value.length;
  
        if ((className == arrayClass || className == stringClass || className == argsClass ) ||
            (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
          return !length;
        }
        forOwn(value, function() {
          return (result = false);
        });
        return result;
      }
  
      /**
       * Performs a deep comparison between two values to determine if they are
       * equivalent to each other. If a callback is provided it will be executed
       * to compare values. If the callback returns `undefined` comparisons will
       * be handled by the method instead. The callback is bound to `thisArg` and
       * invoked with two arguments; (a, b).
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} a The value to compare.
       * @param {*} b The other value to compare.
       * @param {Function} [callback] The function to customize comparing values.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
       * @example
       *
       * var object = { 'name': 'fred' };
       * var copy = { 'name': 'fred' };
       *
       * object == copy;
       * // => false
       *
       * _.isEqual(object, copy);
       * // => true
       *
       * var words = ['hello', 'goodbye'];
       * var otherWords = ['hi', 'goodbye'];
       *
       * _.isEqual(words, otherWords, function(a, b) {
       *   var reGreet = /^(?:hello|hi)$/i,
       *       aGreet = _.isString(a) && reGreet.test(a),
       *       bGreet = _.isString(b) && reGreet.test(b);
       *
       *   return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
       * });
       * // => true
       */
      function isEqual(a, b, callback, thisArg) {
        return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
      }
  
      /**
       * Checks if `value` is, or can be coerced to, a finite number.
       *
       * Note: This is not the same as native `isFinite` which will return true for
       * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
       * @example
       *
       * _.isFinite(-101);
       * // => true
       *
       * _.isFinite('10');
       * // => true
       *
       * _.isFinite(true);
       * // => false
       *
       * _.isFinite('');
       * // => false
       *
       * _.isFinite(Infinity);
       * // => false
       */
      function isFinite(value) {
        return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
      }
  
      /**
       * Checks if `value` is a function.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
       * @example
       *
       * _.isFunction(_);
       * // => true
       */
      function isFunction(value) {
        return typeof value == 'function';
      }
  
      /**
       * Checks if `value` is the language type of Object.
       * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
       * @example
       *
       * _.isObject({});
       * // => true
       *
       * _.isObject([1, 2, 3]);
       * // => true
       *
       * _.isObject(1);
       * // => false
       */
      function isObject(value) {
        // check if the value is the ECMAScript language type of Object
        // http://es5.github.io/#x8
        // and avoid a V8 bug
        // http://code.google.com/p/v8/issues/detail?id=2291
        return !!(value && objectTypes[typeof value]);
      }
  
      /**
       * Checks if `value` is `NaN`.
       *
       * Note: This is not the same as native `isNaN` which will return `true` for
       * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
       * @example
       *
       * _.isNaN(NaN);
       * // => true
       *
       * _.isNaN(new Number(NaN));
       * // => true
       *
       * isNaN(undefined);
       * // => true
       *
       * _.isNaN(undefined);
       * // => false
       */
      function isNaN(value) {
        // `NaN` as a primitive is the only value that is not equal to itself
        // (perform the [[Class]] check first to avoid errors with some host objects in IE)
        return isNumber(value) && value != +value;
      }
  
      /**
       * Checks if `value` is `null`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
       * @example
       *
       * _.isNull(null);
       * // => true
       *
       * _.isNull(undefined);
       * // => false
       */
      function isNull(value) {
        return value === null;
      }
  
      /**
       * Checks if `value` is a number.
       *
       * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
       * @example
       *
       * _.isNumber(8.4 * 5);
       * // => true
       */
      function isNumber(value) {
        return typeof value == 'number' ||
          value && typeof value == 'object' && toString.call(value) == numberClass || false;
      }
  
      /**
       * Checks if `value` is an object created by the `Object` constructor.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
       * @example
       *
       * function Shape() {
       *   this.x = 0;
       *   this.y = 0;
       * }
       *
       * _.isPlainObject(new Shape);
       * // => false
       *
       * _.isPlainObject([1, 2, 3]);
       * // => false
       *
       * _.isPlainObject({ 'x': 0, 'y': 0 });
       * // => true
       */
      var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
        if (!(value && toString.call(value) == objectClass)) {
          return false;
        }
        var valueOf = value.valueOf,
            objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
  
        return objProto
          ? (value == objProto || getPrototypeOf(value) == objProto)
          : shimIsPlainObject(value);
      };
  
      /**
       * Checks if `value` is a regular expression.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
       * @example
       *
       * _.isRegExp(/fred/);
       * // => true
       */
      function isRegExp(value) {
        return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
      }
  
      /**
       * Checks if `value` is a string.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
       * @example
       *
       * _.isString('fred');
       * // => true
       */
      function isString(value) {
        return typeof value == 'string' ||
          value && typeof value == 'object' && toString.call(value) == stringClass || false;
      }
  
      /**
       * Checks if `value` is `undefined`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
       * @example
       *
       * _.isUndefined(void 0);
       * // => true
       */
      function isUndefined(value) {
        return typeof value == 'undefined';
      }
  
      /**
       * Creates an object with the same keys as `object` and values generated by
       * running each own enumerable property of `object` through the callback.
       * The callback is bound to `thisArg` and invoked with three arguments;
       * (value, key, object).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new object with values of the results of each `callback` execution.
       * @example
       *
       * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
       * // => { 'a': 3, 'b': 6, 'c': 9 }
       *
       * var characters = {
       *   'fred': { 'name': 'fred', 'age': 40 },
       *   'pebbles': { 'name': 'pebbles', 'age': 1 }
       * };
       *
       * // using "_.pluck" callback shorthand
       * _.mapValues(characters, 'age');
       * // => { 'fred': 40, 'pebbles': 1 }
       */
      function mapValues(object, callback, thisArg) {
        var result = {};
        callback = lodash.createCallback(callback, thisArg, 3);
  
        forOwn(object, function(value, key, object) {
          result[key] = callback(value, key, object);
        });
        return result;
      }
  
      /**
       * Recursively merges own enumerable properties of the source object(s), that
       * don't resolve to `undefined` into the destination object. Subsequent sources
       * will overwrite property assignments of previous sources. If a callback is
       * provided it will be executed to produce the merged values of the destination
       * and source properties. If the callback returns `undefined` merging will
       * be handled by the method instead. The callback is bound to `thisArg` and
       * invoked with two arguments; (objectValue, sourceValue).
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The destination object.
       * @param {...Object} [source] The source objects.
       * @param {Function} [callback] The function to customize merging properties.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns the destination object.
       * @example
       *
       * var names = {
       *   'characters': [
       *     { 'name': 'barney' },
       *     { 'name': 'fred' }
       *   ]
       * };
       *
       * var ages = {
       *   'characters': [
       *     { 'age': 36 },
       *     { 'age': 40 }
       *   ]
       * };
       *
       * _.merge(names, ages);
       * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
       *
       * var food = {
       *   'fruits': ['apple'],
       *   'vegetables': ['beet']
       * };
       *
       * var otherFood = {
       *   'fruits': ['banana'],
       *   'vegetables': ['carrot']
       * };
       *
       * _.merge(food, otherFood, function(a, b) {
       *   return _.isArray(a) ? a.concat(b) : undefined;
       * });
       * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
       */
      function merge(object) {
        var args = arguments,
            length = 2;
  
        if (!isObject(object)) {
          return object;
        }
        // allows working with `_.reduce` and `_.reduceRight` without using
        // their `index` and `collection` arguments
        if (typeof args[2] != 'number') {
          length = args.length;
        }
        if (length > 3 && typeof args[length - 2] == 'function') {
          var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
        } else if (length > 2 && typeof args[length - 1] == 'function') {
          callback = args[--length];
        }
        var sources = slice(arguments, 1, length),
            index = -1,
            stackA = getArray(),
            stackB = getArray();
  
        while (++index < length) {
          baseMerge(object, sources[index], callback, stackA, stackB);
        }
        releaseArray(stackA);
        releaseArray(stackB);
        return object;
      }
  
      /**
       * Creates a shallow clone of `object` excluding the specified properties.
       * Property names may be specified as individual arguments or as arrays of
       * property names. If a callback is provided it will be executed for each
       * property of `object` omitting the properties the callback returns truey
       * for. The callback is bound to `thisArg` and invoked with three arguments;
       * (value, key, object).
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The source object.
       * @param {Function|...string|string[]} [callback] The properties to omit or the
       *  function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns an object without the omitted properties.
       * @example
       *
       * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
       * // => { 'name': 'fred' }
       *
       * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
       *   return typeof value == 'number';
       * });
       * // => { 'name': 'fred' }
       */
      function omit(object, callback, thisArg) {
        var result = {};
        if (typeof callback != 'function') {
          var props = [];
          forIn(object, function(value, key) {
            props.push(key);
          });
          props = baseDifference(props, baseFlatten(arguments, true, false, 1));
  
          var index = -1,
              length = props.length;
  
          while (++index < length) {
            var key = props[index];
            result[key] = object[key];
          }
        } else {
          callback = lodash.createCallback(callback, thisArg, 3);
          forIn(object, function(value, key, object) {
            if (!callback(value, key, object)) {
              result[key] = value;
            }
          });
        }
        return result;
      }
  
      /**
       * Creates a two dimensional array of an object's key-value pairs,
       * i.e. `[[key1, value1], [key2, value2]]`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to inspect.
       * @returns {Array} Returns new array of key-value pairs.
       * @example
       *
       * _.pairs({ 'barney': 36, 'fred': 40 });
       * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
       */
      function pairs(object) {
        var index = -1,
            props = keys(object),
            length = props.length,
            result = Array(length);
  
        while (++index < length) {
          var key = props[index];
          result[index] = [key, object[key]];
        }
        return result;
      }
  
      /**
       * Creates a shallow clone of `object` composed of the specified properties.
       * Property names may be specified as individual arguments or as arrays of
       * property names. If a callback is provided it will be executed for each
       * property of `object` picking the properties the callback returns truey
       * for. The callback is bound to `thisArg` and invoked with three arguments;
       * (value, key, object).
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The source object.
       * @param {Function|...string|string[]} [callback] The function called per
       *  iteration or property names to pick, specified as individual property
       *  names or arrays of property names.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns an object composed of the picked properties.
       * @example
       *
       * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
       * // => { 'name': 'fred' }
       *
       * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
       *   return key.charAt(0) != '_';
       * });
       * // => { 'name': 'fred' }
       */
      function pick(object, callback, thisArg) {
        var result = {};
        if (typeof callback != 'function') {
          var index = -1,
              props = baseFlatten(arguments, true, false, 1),
              length = isObject(object) ? props.length : 0;
  
          while (++index < length) {
            var key = props[index];
            if (key in object) {
              result[key] = object[key];
            }
          }
        } else {
          callback = lodash.createCallback(callback, thisArg, 3);
          forIn(object, function(value, key, object) {
            if (callback(value, key, object)) {
              result[key] = value;
            }
          });
        }
        return result;
      }
  
      /**
       * An alternative to `_.reduce` this method transforms `object` to a new
       * `accumulator` object which is the result of running each of its own
       * enumerable properties through a callback, with each callback execution
       * potentially mutating the `accumulator` object. The callback is bound to
       * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
       * Callbacks may exit iteration early by explicitly returning `false`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Array|Object} object The object to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [accumulator] The custom accumulator value.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the accumulated value.
       * @example
       *
       * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
       *   num *= num;
       *   if (num % 2) {
       *     return result.push(num) < 3;
       *   }
       * });
       * // => [1, 9, 25]
       *
       * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
       *   result[key] = num * 3;
       * });
       * // => { 'a': 3, 'b': 6, 'c': 9 }
       */
      function transform(object, callback, accumulator, thisArg) {
        var isArr = isArray(object);
        if (accumulator == null) {
          if (isArr) {
            accumulator = [];
          } else {
            var ctor = object && object.constructor,
                proto = ctor && ctor.prototype;
  
            accumulator = baseCreate(proto);
          }
        }
        if (callback) {
          callback = lodash.createCallback(callback, thisArg, 4);
          (isArr ? forEach : forOwn)(object, function(value, index, object) {
            return callback(accumulator, value, index, object);
          });
        }
        return accumulator;
      }
  
      /**
       * Creates an array composed of the own enumerable property values of `object`.
       *
       * @static
       * @memberOf _
       * @category Objects
       * @param {Object} object The object to inspect.
       * @returns {Array} Returns an array of property values.
       * @example
       *
       * _.values({ 'one': 1, 'two': 2, 'three': 3 });
       * // => [1, 2, 3] (property order is not guaranteed across environments)
       */
      function values(object) {
        var index = -1,
            props = keys(object),
            length = props.length,
            result = Array(length);
  
        while (++index < length) {
          result[index] = object[props[index]];
        }
        return result;
      }
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates an array of elements from the specified indexes, or keys, of the
       * `collection`. Indexes may be specified as individual arguments or as arrays
       * of indexes.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
       *   to retrieve, specified as individual indexes or arrays of indexes.
       * @returns {Array} Returns a new array of elements corresponding to the
       *  provided indexes.
       * @example
       *
       * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
       * // => ['a', 'c', 'e']
       *
       * _.at(['fred', 'barney', 'pebbles'], 0, 2);
       * // => ['fred', 'pebbles']
       */
      function at(collection) {
        var args = arguments,
            index = -1,
            props = baseFlatten(args, true, false, 1),
            length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
            result = Array(length);
  
        while(++index < length) {
          result[index] = collection[props[index]];
        }
        return result;
      }
  
      /**
       * Checks if a given value is present in a collection using strict equality
       * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
       * offset from the end of the collection.
       *
       * @static
       * @memberOf _
       * @alias include
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {*} target The value to check for.
       * @param {number} [fromIndex=0] The index to search from.
       * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
       * @example
       *
       * _.contains([1, 2, 3], 1);
       * // => true
       *
       * _.contains([1, 2, 3], 1, 2);
       * // => false
       *
       * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
       * // => true
       *
       * _.contains('pebbles', 'eb');
       * // => true
       */
      function contains(collection, target, fromIndex) {
        var index = -1,
            indexOf = getIndexOf(),
            length = collection ? collection.length : 0,
            result = false;
  
        fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
        if (isArray(collection)) {
          result = indexOf(collection, target, fromIndex) > -1;
        } else if (typeof length == 'number') {
          result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
        } else {
          forOwn(collection, function(value) {
            if (++index >= fromIndex) {
              return !(result = value === target);
            }
          });
        }
        return result;
      }
  
      /**
       * Creates an object composed of keys generated from the results of running
       * each element of `collection` through the callback. The corresponding value
       * of each key is the number of times the key was returned by the callback.
       * The callback is bound to `thisArg` and invoked with three arguments;
       * (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns the composed aggregate object.
       * @example
       *
       * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
       * // => { '4': 1, '6': 2 }
       *
       * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
       * // => { '4': 1, '6': 2 }
       *
       * _.countBy(['one', 'two', 'three'], 'length');
       * // => { '3': 2, '5': 1 }
       */
      var countBy = createAggregator(function(result, value, key) {
        (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
      });
  
      /**
       * Checks if the given callback returns truey value for **all** elements of
       * a collection. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias all
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {boolean} Returns `true` if all elements passed the callback check,
       *  else `false`.
       * @example
       *
       * _.every([true, 1, null, 'yes']);
       * // => false
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.every(characters, 'age');
       * // => true
       *
       * // using "_.where" callback shorthand
       * _.every(characters, { 'age': 36 });
       * // => false
       */
      function every(collection, callback, thisArg) {
        var result = true;
        callback = lodash.createCallback(callback, thisArg, 3);
  
        var index = -1,
            length = collection ? collection.length : 0;
  
        if (typeof length == 'number') {
          while (++index < length) {
            if (!(result = !!callback(collection[index], index, collection))) {
              break;
            }
          }
        } else {
          forOwn(collection, function(value, index, collection) {
            return (result = !!callback(value, index, collection));
          });
        }
        return result;
      }
  
      /**
       * Iterates over elements of a collection, returning an array of all elements
       * the callback returns truey for. The callback is bound to `thisArg` and
       * invoked with three arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias select
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new array of elements that passed the callback check.
       * @example
       *
       * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
       * // => [2, 4, 6]
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36, 'blocked': false },
       *   { 'name': 'fred',   'age': 40, 'blocked': true }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.filter(characters, 'blocked');
       * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
       *
       * // using "_.where" callback shorthand
       * _.filter(characters, { 'age': 36 });
       * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
       */
      function filter(collection, callback, thisArg) {
        var result = [];
        callback = lodash.createCallback(callback, thisArg, 3);
  
        var index = -1,
            length = collection ? collection.length : 0;
  
        if (typeof length == 'number') {
          while (++index < length) {
            var value = collection[index];
            if (callback(value, index, collection)) {
              result.push(value);
            }
          }
        } else {
          forOwn(collection, function(value, index, collection) {
            if (callback(value, index, collection)) {
              result.push(value);
            }
          });
        }
        return result;
      }
  
      /**
       * Iterates over elements of a collection, returning the first element that
       * the callback returns truey for. The callback is bound to `thisArg` and
       * invoked with three arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias detect, findWhere
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the found element, else `undefined`.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney',  'age': 36, 'blocked': false },
       *   { 'name': 'fred',    'age': 40, 'blocked': true },
       *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
       * ];
       *
       * _.find(characters, function(chr) {
       *   return chr.age < 40;
       * });
       * // => { 'name': 'barney', 'age': 36, 'blocked': false }
       *
       * // using "_.where" callback shorthand
       * _.find(characters, { 'age': 1 });
       * // =>  { 'name': 'pebbles', 'age': 1, 'blocked': false }
       *
       * // using "_.pluck" callback shorthand
       * _.find(characters, 'blocked');
       * // => { 'name': 'fred', 'age': 40, 'blocked': true }
       */
      function find(collection, callback, thisArg) {
        callback = lodash.createCallback(callback, thisArg, 3);
  
        var index = -1,
            length = collection ? collection.length : 0;
  
        if (typeof length == 'number') {
          while (++index < length) {
            var value = collection[index];
            if (callback(value, index, collection)) {
              return value;
            }
          }
        } else {
          var result;
          forOwn(collection, function(value, index, collection) {
            if (callback(value, index, collection)) {
              result = value;
              return false;
            }
          });
          return result;
        }
      }
  
      /**
       * This method is like `_.find` except that it iterates over elements
       * of a `collection` from right to left.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the found element, else `undefined`.
       * @example
       *
       * _.findLast([1, 2, 3, 4], function(num) {
       *   return num % 2 == 1;
       * });
       * // => 3
       */
      function findLast(collection, callback, thisArg) {
        var result;
        callback = lodash.createCallback(callback, thisArg, 3);
        forEachRight(collection, function(value, index, collection) {
          if (callback(value, index, collection)) {
            result = value;
            return false;
          }
        });
        return result;
      }
  
      /**
       * Iterates over elements of a collection, executing the callback for each
       * element. The callback is bound to `thisArg` and invoked with three arguments;
       * (value, index|key, collection). Callbacks may exit iteration early by
       * explicitly returning `false`.
       *
       * Note: As with other "Collections" methods, objects with a `length` property
       * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
       * may be used for object iteration.
       *
       * @static
       * @memberOf _
       * @alias each
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array|Object|string} Returns `collection`.
       * @example
       *
       * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
       * // => logs each number and returns '1,2,3'
       *
       * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
       * // => logs each number and returns the object (property order is not guaranteed across environments)
       */
      function forEach(collection, callback, thisArg) {
        var index = -1,
            length = collection ? collection.length : 0;
  
        callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
        if (typeof length == 'number') {
          while (++index < length) {
            if (callback(collection[index], index, collection) === false) {
              break;
            }
          }
        } else {
          forOwn(collection, callback);
        }
        return collection;
      }
  
      /**
       * This method is like `_.forEach` except that it iterates over elements
       * of a `collection` from right to left.
       *
       * @static
       * @memberOf _
       * @alias eachRight
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array|Object|string} Returns `collection`.
       * @example
       *
       * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
       * // => logs each number from right to left and returns '3,2,1'
       */
      function forEachRight(collection, callback, thisArg) {
        var length = collection ? collection.length : 0;
        callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
        if (typeof length == 'number') {
          while (length--) {
            if (callback(collection[length], length, collection) === false) {
              break;
            }
          }
        } else {
          var props = keys(collection);
          length = props.length;
          forOwn(collection, function(value, key, collection) {
            key = props ? props[--length] : --length;
            return callback(collection[key], key, collection);
          });
        }
        return collection;
      }
  
      /**
       * Creates an object composed of keys generated from the results of running
       * each element of a collection through the callback. The corresponding value
       * of each key is an array of the elements responsible for generating the key.
       * The callback is bound to `thisArg` and invoked with three arguments;
       * (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns the composed aggregate object.
       * @example
       *
       * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
       * // => { '4': [4.2], '6': [6.1, 6.4] }
       *
       * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
       * // => { '4': [4.2], '6': [6.1, 6.4] }
       *
       * // using "_.pluck" callback shorthand
       * _.groupBy(['one', 'two', 'three'], 'length');
       * // => { '3': ['one', 'two'], '5': ['three'] }
       */
      var groupBy = createAggregator(function(result, value, key) {
        (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
      });
  
      /**
       * Creates an object composed of keys generated from the results of running
       * each element of the collection through the given callback. The corresponding
       * value of each key is the last element responsible for generating the key.
       * The callback is bound to `thisArg` and invoked with three arguments;
       * (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Object} Returns the composed aggregate object.
       * @example
       *
       * var keys = [
       *   { 'dir': 'left', 'code': 97 },
       *   { 'dir': 'right', 'code': 100 }
       * ];
       *
       * _.indexBy(keys, 'dir');
       * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
       *
       * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
       * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
       *
       * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
       * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
       */
      var indexBy = createAggregator(function(result, value, key) {
        result[key] = value;
      });
  
      /**
       * Invokes the method named by `methodName` on each element in the `collection`
       * returning an array of the results of each invoked method. Additional arguments
       * will be provided to each invoked method. If `methodName` is a function it
       * will be invoked for, and `this` bound to, each element in the `collection`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|string} methodName The name of the method to invoke or
       *  the function invoked per iteration.
       * @param {...*} [arg] Arguments to invoke the method with.
       * @returns {Array} Returns a new array of the results of each invoked method.
       * @example
       *
       * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
       * // => [[1, 5, 7], [1, 2, 3]]
       *
       * _.invoke([123, 456], String.prototype.split, '');
       * // => [['1', '2', '3'], ['4', '5', '6']]
       */
      function invoke(collection, methodName) {
        var args = slice(arguments, 2),
            index = -1,
            isFunc = typeof methodName == 'function',
            length = collection ? collection.length : 0,
            result = Array(typeof length == 'number' ? length : 0);
  
        forEach(collection, function(value) {
          result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
        });
        return result;
      }
  
      /**
       * Creates an array of values by running each element in the collection
       * through the callback. The callback is bound to `thisArg` and invoked with
       * three arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias collect
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new array of the results of each `callback` execution.
       * @example
       *
       * _.map([1, 2, 3], function(num) { return num * 3; });
       * // => [3, 6, 9]
       *
       * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
       * // => [3, 6, 9] (property order is not guaranteed across environments)
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.map(characters, 'name');
       * // => ['barney', 'fred']
       */
      function map(collection, callback, thisArg) {
        var index = -1,
            length = collection ? collection.length : 0;
  
        callback = lodash.createCallback(callback, thisArg, 3);
        if (typeof length == 'number') {
          var result = Array(length);
          while (++index < length) {
            result[index] = callback(collection[index], index, collection);
          }
        } else {
          result = [];
          forOwn(collection, function(value, key, collection) {
            result[++index] = callback(value, key, collection);
          });
        }
        return result;
      }
  
      /**
       * Retrieves the maximum value of a collection. If the collection is empty or
       * falsey `-Infinity` is returned. If a callback is provided it will be executed
       * for each value in the collection to generate the criterion by which the value
       * is ranked. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, index, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the maximum value.
       * @example
       *
       * _.max([4, 2, 8, 6]);
       * // => 8
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * _.max(characters, function(chr) { return chr.age; });
       * // => { 'name': 'fred', 'age': 40 };
       *
       * // using "_.pluck" callback shorthand
       * _.max(characters, 'age');
       * // => { 'name': 'fred', 'age': 40 };
       */
      function max(collection, callback, thisArg) {
        var computed = -Infinity,
            result = computed;
  
        // allows working with functions like `_.map` without using
        // their `index` argument as a callback
        if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
          callback = null;
        }
        if (callback == null && isArray(collection)) {
          var index = -1,
              length = collection.length;
  
          while (++index < length) {
            var value = collection[index];
            if (value > result) {
              result = value;
            }
          }
        } else {
          callback = (callback == null && isString(collection))
            ? charAtCallback
            : lodash.createCallback(callback, thisArg, 3);
  
          forEach(collection, function(value, index, collection) {
            var current = callback(value, index, collection);
            if (current > computed) {
              computed = current;
              result = value;
            }
          });
        }
        return result;
      }
  
      /**
       * Retrieves the minimum value of a collection. If the collection is empty or
       * falsey `Infinity` is returned. If a callback is provided it will be executed
       * for each value in the collection to generate the criterion by which the value
       * is ranked. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, index, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the minimum value.
       * @example
       *
       * _.min([4, 2, 8, 6]);
       * // => 2
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * _.min(characters, function(chr) { return chr.age; });
       * // => { 'name': 'barney', 'age': 36 };
       *
       * // using "_.pluck" callback shorthand
       * _.min(characters, 'age');
       * // => { 'name': 'barney', 'age': 36 };
       */
      function min(collection, callback, thisArg) {
        var computed = Infinity,
            result = computed;
  
        // allows working with functions like `_.map` without using
        // their `index` argument as a callback
        if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
          callback = null;
        }
        if (callback == null && isArray(collection)) {
          var index = -1,
              length = collection.length;
  
          while (++index < length) {
            var value = collection[index];
            if (value < result) {
              result = value;
            }
          }
        } else {
          callback = (callback == null && isString(collection))
            ? charAtCallback
            : lodash.createCallback(callback, thisArg, 3);
  
          forEach(collection, function(value, index, collection) {
            var current = callback(value, index, collection);
            if (current < computed) {
              computed = current;
              result = value;
            }
          });
        }
        return result;
      }
  
      /**
       * Retrieves the value of a specified property from all elements in the collection.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {string} property The name of the property to pluck.
       * @returns {Array} Returns a new array of property values.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * _.pluck(characters, 'name');
       * // => ['barney', 'fred']
       */
      var pluck = map;
  
      /**
       * Reduces a collection to a value which is the accumulated result of running
       * each element in the collection through the callback, where each successive
       * callback execution consumes the return value of the previous execution. If
       * `accumulator` is not provided the first element of the collection will be
       * used as the initial `accumulator` value. The callback is bound to `thisArg`
       * and invoked with four arguments; (accumulator, value, index|key, collection).
       *
       * @static
       * @memberOf _
       * @alias foldl, inject
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [accumulator] Initial value of the accumulator.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the accumulated value.
       * @example
       *
       * var sum = _.reduce([1, 2, 3], function(sum, num) {
       *   return sum + num;
       * });
       * // => 6
       *
       * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
       *   result[key] = num * 3;
       *   return result;
       * }, {});
       * // => { 'a': 3, 'b': 6, 'c': 9 }
       */
      function reduce(collection, callback, accumulator, thisArg) {
        if (!collection) return accumulator;
        var noaccum = arguments.length < 3;
        callback = lodash.createCallback(callback, thisArg, 4);
  
        var index = -1,
            length = collection.length;
  
        if (typeof length == 'number') {
          if (noaccum) {
            accumulator = collection[++index];
          }
          while (++index < length) {
            accumulator = callback(accumulator, collection[index], index, collection);
          }
        } else {
          forOwn(collection, function(value, index, collection) {
            accumulator = noaccum
              ? (noaccum = false, value)
              : callback(accumulator, value, index, collection)
          });
        }
        return accumulator;
      }
  
      /**
       * This method is like `_.reduce` except that it iterates over elements
       * of a `collection` from right to left.
       *
       * @static
       * @memberOf _
       * @alias foldr
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function} [callback=identity] The function called per iteration.
       * @param {*} [accumulator] Initial value of the accumulator.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the accumulated value.
       * @example
       *
       * var list = [[0, 1], [2, 3], [4, 5]];
       * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
       * // => [4, 5, 2, 3, 0, 1]
       */
      function reduceRight(collection, callback, accumulator, thisArg) {
        var noaccum = arguments.length < 3;
        callback = lodash.createCallback(callback, thisArg, 4);
        forEachRight(collection, function(value, index, collection) {
          accumulator = noaccum
            ? (noaccum = false, value)
            : callback(accumulator, value, index, collection);
        });
        return accumulator;
      }
  
      /**
       * The opposite of `_.filter` this method returns the elements of a
       * collection that the callback does **not** return truey for.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new array of elements that failed the callback check.
       * @example
       *
       * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
       * // => [1, 3, 5]
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36, 'blocked': false },
       *   { 'name': 'fred',   'age': 40, 'blocked': true }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.reject(characters, 'blocked');
       * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
       *
       * // using "_.where" callback shorthand
       * _.reject(characters, { 'age': 36 });
       * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
       */
      function reject(collection, callback, thisArg) {
        callback = lodash.createCallback(callback, thisArg, 3);
        return filter(collection, function(value, index, collection) {
          return !callback(value, index, collection);
        });
      }
  
      /**
       * Retrieves a random element or `n` random elements from a collection.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to sample.
       * @param {number} [n] The number of elements to sample.
       * @param- {Object} [guard] Allows working with functions like `_.map`
       *  without using their `index` arguments as `n`.
       * @returns {Array} Returns the random sample(s) of `collection`.
       * @example
       *
       * _.sample([1, 2, 3, 4]);
       * // => 2
       *
       * _.sample([1, 2, 3, 4], 2);
       * // => [3, 1]
       */
      function sample(collection, n, guard) {
        if (collection && typeof collection.length != 'number') {
          collection = values(collection);
        }
        if (n == null || guard) {
          return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
        }
        var result = shuffle(collection);
        result.length = nativeMin(nativeMax(0, n), result.length);
        return result;
      }
  
      /**
       * Creates an array of shuffled values, using a version of the Fisher-Yates
       * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to shuffle.
       * @returns {Array} Returns a new shuffled collection.
       * @example
       *
       * _.shuffle([1, 2, 3, 4, 5, 6]);
       * // => [4, 1, 6, 3, 5, 2]
       */
      function shuffle(collection) {
        var index = -1,
            length = collection ? collection.length : 0,
            result = Array(typeof length == 'number' ? length : 0);
  
        forEach(collection, function(value) {
          var rand = baseRandom(0, ++index);
          result[index] = result[rand];
          result[rand] = value;
        });
        return result;
      }
  
      /**
       * Gets the size of the `collection` by returning `collection.length` for arrays
       * and array-like objects or the number of own enumerable properties for objects.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to inspect.
       * @returns {number} Returns `collection.length` or number of own enumerable properties.
       * @example
       *
       * _.size([1, 2]);
       * // => 2
       *
       * _.size({ 'one': 1, 'two': 2, 'three': 3 });
       * // => 3
       *
       * _.size('pebbles');
       * // => 7
       */
      function size(collection) {
        var length = collection ? collection.length : 0;
        return typeof length == 'number' ? length : keys(collection).length;
      }
  
      /**
       * Checks if the callback returns a truey value for **any** element of a
       * collection. The function returns as soon as it finds a passing value and
       * does not iterate over the entire collection. The callback is bound to
       * `thisArg` and invoked with three arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias any
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {boolean} Returns `true` if any element passed the callback check,
       *  else `false`.
       * @example
       *
       * _.some([null, 0, 'yes', false], Boolean);
       * // => true
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36, 'blocked': false },
       *   { 'name': 'fred',   'age': 40, 'blocked': true }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.some(characters, 'blocked');
       * // => true
       *
       * // using "_.where" callback shorthand
       * _.some(characters, { 'age': 1 });
       * // => false
       */
      function some(collection, callback, thisArg) {
        var result;
        callback = lodash.createCallback(callback, thisArg, 3);
  
        var index = -1,
            length = collection ? collection.length : 0;
  
        if (typeof length == 'number') {
          while (++index < length) {
            if ((result = callback(collection[index], index, collection))) {
              break;
            }
          }
        } else {
          forOwn(collection, function(value, index, collection) {
            return !(result = callback(value, index, collection));
          });
        }
        return !!result;
      }
  
      /**
       * Creates an array of elements, sorted in ascending order by the results of
       * running each element in a collection through the callback. This method
       * performs a stable sort, that is, it will preserve the original sort order
       * of equal elements. The callback is bound to `thisArg` and invoked with
       * three arguments; (value, index|key, collection).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an array of property names is provided for `callback` the collection
       * will be sorted by each property value.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Array|Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new array of sorted elements.
       * @example
       *
       * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
       * // => [3, 1, 2]
       *
       * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
       * // => [3, 1, 2]
       *
       * var characters = [
       *   { 'name': 'barney',  'age': 36 },
       *   { 'name': 'fred',    'age': 40 },
       *   { 'name': 'barney',  'age': 26 },
       *   { 'name': 'fred',    'age': 30 }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.map(_.sortBy(characters, 'age'), _.values);
       * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
       *
       * // sorting by multiple properties
       * _.map(_.sortBy(characters, ['name', 'age']), _.values);
       * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
       */
      function sortBy(collection, callback, thisArg) {
        var index = -1,
            isArr = isArray(callback),
            length = collection ? collection.length : 0,
            result = Array(typeof length == 'number' ? length : 0);
  
        if (!isArr) {
          callback = lodash.createCallback(callback, thisArg, 3);
        }
        forEach(collection, function(value, key, collection) {
          var object = result[++index] = getObject();
          if (isArr) {
            object.criteria = map(callback, function(key) { return value[key]; });
          } else {
            (object.criteria = getArray())[0] = callback(value, key, collection);
          }
          object.index = index;
          object.value = value;
        });
  
        length = result.length;
        result.sort(compareAscending);
        while (length--) {
          var object = result[length];
          result[length] = object.value;
          if (!isArr) {
            releaseArray(object.criteria);
          }
          releaseObject(object);
        }
        return result;
      }
  
      /**
       * Converts the `collection` to an array.
       *
       * @static
       * @memberOf _
       * @category Collections
       * @param {Array|Object|string} collection The collection to convert.
       * @returns {Array} Returns the new converted array.
       * @example
       *
       * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
       * // => [2, 3, 4]
       */
      function toArray(collection) {
        if (collection && typeof collection.length == 'number') {
          return slice(collection);
        }
        return values(collection);
      }
  
      /**
       * Performs a deep comparison of each element in a `collection` to the given
       * `properties` object, returning an array of all elements that have equivalent
       * property values.
       *
       * @static
       * @memberOf _
       * @type Function
       * @category Collections
       * @param {Array|Object|string} collection The collection to iterate over.
       * @param {Object} props The object of property values to filter by.
       * @returns {Array} Returns a new array of elements that have the given properties.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
       *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
       * ];
       *
       * _.where(characters, { 'age': 36 });
       * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
       *
       * _.where(characters, { 'pets': ['dino'] });
       * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
       */
      var where = filter;
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates an array with all falsey values removed. The values `false`, `null`,
       * `0`, `""`, `undefined`, and `NaN` are all falsey.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to compact.
       * @returns {Array} Returns a new array of filtered values.
       * @example
       *
       * _.compact([0, 1, false, 2, '', 3]);
       * // => [1, 2, 3]
       */
      function compact(array) {
        var index = -1,
            length = array ? array.length : 0,
            result = [];
  
        while (++index < length) {
          var value = array[index];
          if (value) {
            result.push(value);
          }
        }
        return result;
      }
  
      /**
       * Creates an array excluding all values of the provided arrays using strict
       * equality for comparisons, i.e. `===`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to process.
       * @param {...Array} [values] The arrays of values to exclude.
       * @returns {Array} Returns a new array of filtered values.
       * @example
       *
       * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
       * // => [1, 3, 4]
       */
      function difference(array) {
        return baseDifference(array, baseFlatten(arguments, true, true, 1));
      }
  
      /**
       * This method is like `_.find` except that it returns the index of the first
       * element that passes the callback check, instead of the element itself.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to search.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {number} Returns the index of the found element, else `-1`.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney',  'age': 36, 'blocked': false },
       *   { 'name': 'fred',    'age': 40, 'blocked': true },
       *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
       * ];
       *
       * _.findIndex(characters, function(chr) {
       *   return chr.age < 20;
       * });
       * // => 2
       *
       * // using "_.where" callback shorthand
       * _.findIndex(characters, { 'age': 36 });
       * // => 0
       *
       * // using "_.pluck" callback shorthand
       * _.findIndex(characters, 'blocked');
       * // => 1
       */
      function findIndex(array, callback, thisArg) {
        var index = -1,
            length = array ? array.length : 0;
  
        callback = lodash.createCallback(callback, thisArg, 3);
        while (++index < length) {
          if (callback(array[index], index, array)) {
            return index;
          }
        }
        return -1;
      }
  
      /**
       * This method is like `_.findIndex` except that it iterates over elements
       * of a `collection` from right to left.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to search.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {number} Returns the index of the found element, else `-1`.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney',  'age': 36, 'blocked': true },
       *   { 'name': 'fred',    'age': 40, 'blocked': false },
       *   { 'name': 'pebbles', 'age': 1,  'blocked': true }
       * ];
       *
       * _.findLastIndex(characters, function(chr) {
       *   return chr.age > 30;
       * });
       * // => 1
       *
       * // using "_.where" callback shorthand
       * _.findLastIndex(characters, { 'age': 36 });
       * // => 0
       *
       * // using "_.pluck" callback shorthand
       * _.findLastIndex(characters, 'blocked');
       * // => 2
       */
      function findLastIndex(array, callback, thisArg) {
        var length = array ? array.length : 0;
        callback = lodash.createCallback(callback, thisArg, 3);
        while (length--) {
          if (callback(array[length], length, array)) {
            return length;
          }
        }
        return -1;
      }
  
      /**
       * Gets the first element or first `n` elements of an array. If a callback
       * is provided elements at the beginning of the array are returned as long
       * as the callback returns truey. The callback is bound to `thisArg` and
       * invoked with three arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias head, take
       * @category Arrays
       * @param {Array} array The array to query.
       * @param {Function|Object|number|string} [callback] The function called
       *  per element or the number of elements to return. If a property name or
       *  object is provided it will be used to create a "_.pluck" or "_.where"
       *  style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the first element(s) of `array`.
       * @example
       *
       * _.first([1, 2, 3]);
       * // => 1
       *
       * _.first([1, 2, 3], 2);
       * // => [1, 2]
       *
       * _.first([1, 2, 3], function(num) {
       *   return num < 3;
       * });
       * // => [1, 2]
       *
       * var characters = [
       *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
       *   { 'name': 'fred',    'blocked': false, 'employer': 'slate' },
       *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.first(characters, 'blocked');
       * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
       *
       * // using "_.where" callback shorthand
       * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
       * // => ['barney', 'fred']
       */
      function first(array, callback, thisArg) {
        var n = 0,
            length = array ? array.length : 0;
  
        if (typeof callback != 'number' && callback != null) {
          var index = -1;
          callback = lodash.createCallback(callback, thisArg, 3);
          while (++index < length && callback(array[index], index, array)) {
            n++;
          }
        } else {
          n = callback;
          if (n == null || thisArg) {
            return array ? array[0] : undefined;
          }
        }
        return slice(array, 0, nativeMin(nativeMax(0, n), length));
      }
  
      /**
       * Flattens a nested array (the nesting can be to any depth). If `isShallow`
       * is truey, the array will only be flattened a single level. If a callback
       * is provided each element of the array is passed through the callback before
       * flattening. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to flatten.
       * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new flattened array.
       * @example
       *
       * _.flatten([1, [2], [3, [[4]]]]);
       * // => [1, 2, 3, 4];
       *
       * _.flatten([1, [2], [3, [[4]]]], true);
       * // => [1, 2, 3, [[4]]];
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
       *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.flatten(characters, 'pets');
       * // => ['hoppy', 'baby puss', 'dino']
       */
      function flatten(array, isShallow, callback, thisArg) {
        // juggle arguments
        if (typeof isShallow != 'boolean' && isShallow != null) {
          thisArg = callback;
          callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
          isShallow = false;
        }
        if (callback != null) {
          array = map(array, callback, thisArg);
        }
        return baseFlatten(array, isShallow);
      }
  
      /**
       * Gets the index at which the first occurrence of `value` is found using
       * strict equality for comparisons, i.e. `===`. If the array is already sorted
       * providing `true` for `fromIndex` will run a faster binary search.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to search.
       * @param {*} value The value to search for.
       * @param {boolean|number} [fromIndex=0] The index to search from or `true`
       *  to perform a binary search on a sorted array.
       * @returns {number} Returns the index of the matched value or `-1`.
       * @example
       *
       * _.indexOf([1, 2, 3, 1, 2, 3], 2);
       * // => 1
       *
       * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
       * // => 4
       *
       * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
       * // => 2
       */
      function indexOf(array, value, fromIndex) {
        if (typeof fromIndex == 'number') {
          var length = array ? array.length : 0;
          fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
        } else if (fromIndex) {
          var index = sortedIndex(array, value);
          return array[index] === value ? index : -1;
        }
        return baseIndexOf(array, value, fromIndex);
      }
  
      /**
       * Gets all but the last element or last `n` elements of an array. If a
       * callback is provided elements at the end of the array are excluded from
       * the result as long as the callback returns truey. The callback is bound
       * to `thisArg` and invoked with three arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to query.
       * @param {Function|Object|number|string} [callback=1] The function called
       *  per element or the number of elements to exclude. If a property name or
       *  object is provided it will be used to create a "_.pluck" or "_.where"
       *  style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a slice of `array`.
       * @example
       *
       * _.initial([1, 2, 3]);
       * // => [1, 2]
       *
       * _.initial([1, 2, 3], 2);
       * // => [1]
       *
       * _.initial([1, 2, 3], function(num) {
       *   return num > 1;
       * });
       * // => [1]
       *
       * var characters = [
       *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
       *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
       *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.initial(characters, 'blocked');
       * // => [{ 'name': 'barney',  'blocked': false, 'employer': 'slate' }]
       *
       * // using "_.where" callback shorthand
       * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
       * // => ['barney', 'fred']
       */
      function initial(array, callback, thisArg) {
        var n = 0,
            length = array ? array.length : 0;
  
        if (typeof callback != 'number' && callback != null) {
          var index = length;
          callback = lodash.createCallback(callback, thisArg, 3);
          while (index-- && callback(array[index], index, array)) {
            n++;
          }
        } else {
          n = (callback == null || thisArg) ? 1 : callback || n;
        }
        return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
      }
  
      /**
       * Creates an array of unique values present in all provided arrays using
       * strict equality for comparisons, i.e. `===`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {...Array} [array] The arrays to inspect.
       * @returns {Array} Returns an array of shared values.
       * @example
       *
       * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
       * // => [1, 2]
       */
      function intersection() {
        var args = [],
            argsIndex = -1,
            argsLength = arguments.length,
            caches = getArray(),
            indexOf = getIndexOf(),
            trustIndexOf = indexOf === baseIndexOf,
            seen = getArray();
  
        while (++argsIndex < argsLength) {
          var value = arguments[argsIndex];
          if (isArray(value) || isArguments(value)) {
            args.push(value);
            caches.push(trustIndexOf && value.length >= largeArraySize &&
              createCache(argsIndex ? args[argsIndex] : seen));
          }
        }
        var array = args[0],
            index = -1,
            length = array ? array.length : 0,
            result = [];
  
        outer:
        while (++index < length) {
          var cache = caches[0];
          value = array[index];
  
          if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
            argsIndex = argsLength;
            (cache || seen).push(value);
            while (--argsIndex) {
              cache = caches[argsIndex];
              if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
                continue outer;
              }
            }
            result.push(value);
          }
        }
        while (argsLength--) {
          cache = caches[argsLength];
          if (cache) {
            releaseObject(cache);
          }
        }
        releaseArray(caches);
        releaseArray(seen);
        return result;
      }
  
      /**
       * Gets the last element or last `n` elements of an array. If a callback is
       * provided elements at the end of the array are returned as long as the
       * callback returns truey. The callback is bound to `thisArg` and invoked
       * with three arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to query.
       * @param {Function|Object|number|string} [callback] The function called
       *  per element or the number of elements to return. If a property name or
       *  object is provided it will be used to create a "_.pluck" or "_.where"
       *  style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {*} Returns the last element(s) of `array`.
       * @example
       *
       * _.last([1, 2, 3]);
       * // => 3
       *
       * _.last([1, 2, 3], 2);
       * // => [2, 3]
       *
       * _.last([1, 2, 3], function(num) {
       *   return num > 1;
       * });
       * // => [2, 3]
       *
       * var characters = [
       *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
       *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
       *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.pluck(_.last(characters, 'blocked'), 'name');
       * // => ['fred', 'pebbles']
       *
       * // using "_.where" callback shorthand
       * _.last(characters, { 'employer': 'na' });
       * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
       */
      function last(array, callback, thisArg) {
        var n = 0,
            length = array ? array.length : 0;
  
        if (typeof callback != 'number' && callback != null) {
          var index = length;
          callback = lodash.createCallback(callback, thisArg, 3);
          while (index-- && callback(array[index], index, array)) {
            n++;
          }
        } else {
          n = callback;
          if (n == null || thisArg) {
            return array ? array[length - 1] : undefined;
          }
        }
        return slice(array, nativeMax(0, length - n));
      }
  
      /**
       * Gets the index at which the last occurrence of `value` is found using strict
       * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
       * as the offset from the end of the collection.
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to search.
       * @param {*} value The value to search for.
       * @param {number} [fromIndex=array.length-1] The index to search from.
       * @returns {number} Returns the index of the matched value or `-1`.
       * @example
       *
       * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
       * // => 4
       *
       * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
       * // => 1
       */
      function lastIndexOf(array, value, fromIndex) {
        var index = array ? array.length : 0;
        if (typeof fromIndex == 'number') {
          index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
        }
        while (index--) {
          if (array[index] === value) {
            return index;
          }
        }
        return -1;
      }
  
      /**
       * Removes all provided values from the given array using strict equality for
       * comparisons, i.e. `===`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to modify.
       * @param {...*} [value] The values to remove.
       * @returns {Array} Returns `array`.
       * @example
       *
       * var array = [1, 2, 3, 1, 2, 3];
       * _.pull(array, 2, 3);
       * console.log(array);
       * // => [1, 1]
       */
      function pull(array) {
        var args = arguments,
            argsIndex = 0,
            argsLength = args.length,
            length = array ? array.length : 0;
  
        while (++argsIndex < argsLength) {
          var index = -1,
              value = args[argsIndex];
          while (++index < length) {
            if (array[index] === value) {
              splice.call(array, index--, 1);
              length--;
            }
          }
        }
        return array;
      }
  
      /**
       * Creates an array of numbers (positive and/or negative) progressing from
       * `start` up to but not including `end`. If `start` is less than `stop` a
       * zero-length range is created unless a negative `step` is specified.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {number} [start=0] The start of the range.
       * @param {number} end The end of the range.
       * @param {number} [step=1] The value to increment or decrement by.
       * @returns {Array} Returns a new range array.
       * @example
       *
       * _.range(4);
       * // => [0, 1, 2, 3]
       *
       * _.range(1, 5);
       * // => [1, 2, 3, 4]
       *
       * _.range(0, 20, 5);
       * // => [0, 5, 10, 15]
       *
       * _.range(0, -4, -1);
       * // => [0, -1, -2, -3]
       *
       * _.range(1, 4, 0);
       * // => [1, 1, 1]
       *
       * _.range(0);
       * // => []
       */
      function range(start, end, step) {
        start = +start || 0;
        step = typeof step == 'number' ? step : (+step || 1);
  
        if (end == null) {
          end = start;
          start = 0;
        }
        // use `Array(length)` so engines like Chakra and V8 avoid slower modes
        // http://youtu.be/XAqIpGU8ZZk#t=17m25s
        var index = -1,
            length = nativeMax(0, ceil((end - start) / (step || 1))),
            result = Array(length);
  
        while (++index < length) {
          result[index] = start;
          start += step;
        }
        return result;
      }
  
      /**
       * Removes all elements from an array that the callback returns truey for
       * and returns an array of removed elements. The callback is bound to `thisArg`
       * and invoked with three arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to modify.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a new array of removed elements.
       * @example
       *
       * var array = [1, 2, 3, 4, 5, 6];
       * var evens = _.remove(array, function(num) { return num % 2 == 0; });
       *
       * console.log(array);
       * // => [1, 3, 5]
       *
       * console.log(evens);
       * // => [2, 4, 6]
       */
      function remove(array, callback, thisArg) {
        var index = -1,
            length = array ? array.length : 0,
            result = [];
  
        callback = lodash.createCallback(callback, thisArg, 3);
        while (++index < length) {
          var value = array[index];
          if (callback(value, index, array)) {
            result.push(value);
            splice.call(array, index--, 1);
            length--;
          }
        }
        return result;
      }
  
      /**
       * The opposite of `_.initial` this method gets all but the first element or
       * first `n` elements of an array. If a callback function is provided elements
       * at the beginning of the array are excluded from the result as long as the
       * callback returns truey. The callback is bound to `thisArg` and invoked
       * with three arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias drop, tail
       * @category Arrays
       * @param {Array} array The array to query.
       * @param {Function|Object|number|string} [callback=1] The function called
       *  per element or the number of elements to exclude. If a property name or
       *  object is provided it will be used to create a "_.pluck" or "_.where"
       *  style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a slice of `array`.
       * @example
       *
       * _.rest([1, 2, 3]);
       * // => [2, 3]
       *
       * _.rest([1, 2, 3], 2);
       * // => [3]
       *
       * _.rest([1, 2, 3], function(num) {
       *   return num < 3;
       * });
       * // => [3]
       *
       * var characters = [
       *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
       *   { 'name': 'fred',    'blocked': false,  'employer': 'slate' },
       *   { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
       * ];
       *
       * // using "_.pluck" callback shorthand
       * _.pluck(_.rest(characters, 'blocked'), 'name');
       * // => ['fred', 'pebbles']
       *
       * // using "_.where" callback shorthand
       * _.rest(characters, { 'employer': 'slate' });
       * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
       */
      function rest(array, callback, thisArg) {
        if (typeof callback != 'number' && callback != null) {
          var n = 0,
              index = -1,
              length = array ? array.length : 0;
  
          callback = lodash.createCallback(callback, thisArg, 3);
          while (++index < length && callback(array[index], index, array)) {
            n++;
          }
        } else {
          n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
        }
        return slice(array, n);
      }
  
      /**
       * Uses a binary search to determine the smallest index at which a value
       * should be inserted into a given sorted array in order to maintain the sort
       * order of the array. If a callback is provided it will be executed for
       * `value` and each element of `array` to compute their sort ranking. The
       * callback is bound to `thisArg` and invoked with one argument; (value).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to inspect.
       * @param {*} value The value to evaluate.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {number} Returns the index at which `value` should be inserted
       *  into `array`.
       * @example
       *
       * _.sortedIndex([20, 30, 50], 40);
       * // => 2
       *
       * // using "_.pluck" callback shorthand
       * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
       * // => 2
       *
       * var dict = {
       *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
       * };
       *
       * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
       *   return dict.wordToNumber[word];
       * });
       * // => 2
       *
       * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
       *   return this.wordToNumber[word];
       * }, dict);
       * // => 2
       */
      function sortedIndex(array, value, callback, thisArg) {
        var low = 0,
            high = array ? array.length : low;
  
        // explicitly reference `identity` for better inlining in Firefox
        callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
        value = callback(value);
  
        while (low < high) {
          var mid = (low + high) >>> 1;
          (callback(array[mid]) < value)
            ? low = mid + 1
            : high = mid;
        }
        return low;
      }
  
      /**
       * Creates an array of unique values, in order, of the provided arrays using
       * strict equality for comparisons, i.e. `===`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {...Array} [array] The arrays to inspect.
       * @returns {Array} Returns an array of combined values.
       * @example
       *
       * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
       * // => [1, 2, 3, 5, 4]
       */
      function union() {
        return baseUniq(baseFlatten(arguments, true, true));
      }
  
      /**
       * Creates a duplicate-value-free version of an array using strict equality
       * for comparisons, i.e. `===`. If the array is sorted, providing
       * `true` for `isSorted` will use a faster algorithm. If a callback is provided
       * each element of `array` is passed through the callback before uniqueness
       * is computed. The callback is bound to `thisArg` and invoked with three
       * arguments; (value, index, array).
       *
       * If a property name is provided for `callback` the created "_.pluck" style
       * callback will return the property value of the given element.
       *
       * If an object is provided for `callback` the created "_.where" style callback
       * will return `true` for elements that have the properties of the given object,
       * else `false`.
       *
       * @static
       * @memberOf _
       * @alias unique
       * @category Arrays
       * @param {Array} array The array to process.
       * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
       * @param {Function|Object|string} [callback=identity] The function called
       *  per iteration. If a property name or object is provided it will be used
       *  to create a "_.pluck" or "_.where" style callback, respectively.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns a duplicate-value-free array.
       * @example
       *
       * _.uniq([1, 2, 1, 3, 1]);
       * // => [1, 2, 3]
       *
       * _.uniq([1, 1, 2, 2, 3], true);
       * // => [1, 2, 3]
       *
       * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
       * // => ['A', 'b', 'C']
       *
       * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
       * // => [1, 2.5, 3]
       *
       * // using "_.pluck" callback shorthand
       * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
       * // => [{ 'x': 1 }, { 'x': 2 }]
       */
      function uniq(array, isSorted, callback, thisArg) {
        // juggle arguments
        if (typeof isSorted != 'boolean' && isSorted != null) {
          thisArg = callback;
          callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
          isSorted = false;
        }
        if (callback != null) {
          callback = lodash.createCallback(callback, thisArg, 3);
        }
        return baseUniq(array, isSorted, callback);
      }
  
      /**
       * Creates an array excluding all provided values using strict equality for
       * comparisons, i.e. `===`.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {Array} array The array to filter.
       * @param {...*} [value] The values to exclude.
       * @returns {Array} Returns a new array of filtered values.
       * @example
       *
       * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
       * // => [2, 3, 4]
       */
      function without(array) {
        return baseDifference(array, slice(arguments, 1));
      }
  
      /**
       * Creates an array that is the symmetric difference of the provided arrays.
       * See http://en.wikipedia.org/wiki/Symmetric_difference.
       *
       * @static
       * @memberOf _
       * @category Arrays
       * @param {...Array} [array] The arrays to inspect.
       * @returns {Array} Returns an array of values.
       * @example
       *
       * _.xor([1, 2, 3], [5, 2, 1, 4]);
       * // => [3, 5, 4]
       *
       * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
       * // => [1, 4, 5]
       */
      function xor() {
        var index = -1,
            length = arguments.length;
  
        while (++index < length) {
          var array = arguments[index];
          if (isArray(array) || isArguments(array)) {
            var result = result
              ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
              : array;
          }
        }
        return result || [];
      }
  
      /**
       * Creates an array of grouped elements, the first of which contains the first
       * elements of the given arrays, the second of which contains the second
       * elements of the given arrays, and so on.
       *
       * @static
       * @memberOf _
       * @alias unzip
       * @category Arrays
       * @param {...Array} [array] Arrays to process.
       * @returns {Array} Returns a new array of grouped elements.
       * @example
       *
       * _.zip(['fred', 'barney'], [30, 40], [true, false]);
       * // => [['fred', 30, true], ['barney', 40, false]]
       */
      function zip() {
        var array = arguments.length > 1 ? arguments : arguments[0],
            index = -1,
            length = array ? max(pluck(array, 'length')) : 0,
            result = Array(length < 0 ? 0 : length);
  
        while (++index < length) {
          result[index] = pluck(array, index);
        }
        return result;
      }
  
      /**
       * Creates an object composed from arrays of `keys` and `values`. Provide
       * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
       * or two arrays, one of `keys` and one of corresponding `values`.
       *
       * @static
       * @memberOf _
       * @alias object
       * @category Arrays
       * @param {Array} keys The array of keys.
       * @param {Array} [values=[]] The array of values.
       * @returns {Object} Returns an object composed of the given keys and
       *  corresponding values.
       * @example
       *
       * _.zipObject(['fred', 'barney'], [30, 40]);
       * // => { 'fred': 30, 'barney': 40 }
       */
      function zipObject(keys, values) {
        var index = -1,
            length = keys ? keys.length : 0,
            result = {};
  
        if (!values && length && !isArray(keys[0])) {
          values = [];
        }
        while (++index < length) {
          var key = keys[index];
          if (values) {
            result[key] = values[index];
          } else if (key) {
            result[key[0]] = key[1];
          }
        }
        return result;
      }
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates a function that executes `func`, with  the `this` binding and
       * arguments of the created function, only after being called `n` times.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {number} n The number of times the function must be called before
       *  `func` is executed.
       * @param {Function} func The function to restrict.
       * @returns {Function} Returns the new restricted function.
       * @example
       *
       * var saves = ['profile', 'settings'];
       *
       * var done = _.after(saves.length, function() {
       *   console.log('Done saving!');
       * });
       *
       * _.forEach(saves, function(type) {
       *   asyncSave({ 'type': type, 'complete': done });
       * });
       * // => logs 'Done saving!', after all saves have completed
       */
      function after(n, func) {
        if (!isFunction(func)) {
          throw new TypeError;
        }
        return function() {
          if (--n < 1) {
            return func.apply(this, arguments);
          }
        };
      }
  
      /**
       * Creates a function that, when called, invokes `func` with the `this`
       * binding of `thisArg` and prepends any additional `bind` arguments to those
       * provided to the bound function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to bind.
       * @param {*} [thisArg] The `this` binding of `func`.
       * @param {...*} [arg] Arguments to be partially applied.
       * @returns {Function} Returns the new bound function.
       * @example
       *
       * var func = function(greeting) {
       *   return greeting + ' ' + this.name;
       * };
       *
       * func = _.bind(func, { 'name': 'fred' }, 'hi');
       * func();
       * // => 'hi fred'
       */
      function bind(func, thisArg) {
        return arguments.length > 2
          ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
          : createWrapper(func, 1, null, null, thisArg);
      }
  
      /**
       * Binds methods of an object to the object itself, overwriting the existing
       * method. Method names may be specified as individual arguments or as arrays
       * of method names. If no method names are provided all the function properties
       * of `object` will be bound.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Object} object The object to bind and assign the bound methods to.
       * @param {...string} [methodName] The object method names to
       *  bind, specified as individual method names or arrays of method names.
       * @returns {Object} Returns `object`.
       * @example
       *
       * var view = {
       *   'label': 'docs',
       *   'onClick': function() { console.log('clicked ' + this.label); }
       * };
       *
       * _.bindAll(view);
       * jQuery('#docs').on('click', view.onClick);
       * // => logs 'clicked docs', when the button is clicked
       */
      function bindAll(object) {
        var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
            index = -1,
            length = funcs.length;
  
        while (++index < length) {
          var key = funcs[index];
          object[key] = createWrapper(object[key], 1, null, null, object);
        }
        return object;
      }
  
      /**
       * Creates a function that, when called, invokes the method at `object[key]`
       * and prepends any additional `bindKey` arguments to those provided to the bound
       * function. This method differs from `_.bind` by allowing bound functions to
       * reference methods that will be redefined or don't yet exist.
       * See http://michaux.ca/articles/lazy-function-definition-pattern.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Object} object The object the method belongs to.
       * @param {string} key The key of the method.
       * @param {...*} [arg] Arguments to be partially applied.
       * @returns {Function} Returns the new bound function.
       * @example
       *
       * var object = {
       *   'name': 'fred',
       *   'greet': function(greeting) {
       *     return greeting + ' ' + this.name;
       *   }
       * };
       *
       * var func = _.bindKey(object, 'greet', 'hi');
       * func();
       * // => 'hi fred'
       *
       * object.greet = function(greeting) {
       *   return greeting + 'ya ' + this.name + '!';
       * };
       *
       * func();
       * // => 'hiya fred!'
       */
      function bindKey(object, key) {
        return arguments.length > 2
          ? createWrapper(key, 19, slice(arguments, 2), null, object)
          : createWrapper(key, 3, null, null, object);
      }
  
      /**
       * Creates a function that is the composition of the provided functions,
       * where each function consumes the return value of the function that follows.
       * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
       * Each function is executed with the `this` binding of the composed function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {...Function} [func] Functions to compose.
       * @returns {Function} Returns the new composed function.
       * @example
       *
       * var realNameMap = {
       *   'pebbles': 'penelope'
       * };
       *
       * var format = function(name) {
       *   name = realNameMap[name.toLowerCase()] || name;
       *   return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
       * };
       *
       * var greet = function(formatted) {
       *   return 'Hiya ' + formatted + '!';
       * };
       *
       * var welcome = _.compose(greet, format);
       * welcome('pebbles');
       * // => 'Hiya Penelope!'
       */
      function compose() {
        var funcs = arguments,
            length = funcs.length;
  
        while (length--) {
          if (!isFunction(funcs[length])) {
            throw new TypeError;
          }
        }
        return function() {
          var args = arguments,
              length = funcs.length;
  
          while (length--) {
            args = [funcs[length].apply(this, args)];
          }
          return args[0];
        };
      }
  
      /**
       * Creates a function which accepts one or more arguments of `func` that when
       * invoked either executes `func` returning its result, if all `func` arguments
       * have been provided, or returns a function that accepts one or more of the
       * remaining `func` arguments, and so on. The arity of `func` can be specified
       * if `func.length` is not sufficient.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to curry.
       * @param {number} [arity=func.length] The arity of `func`.
       * @returns {Function} Returns the new curried function.
       * @example
       *
       * var curried = _.curry(function(a, b, c) {
       *   console.log(a + b + c);
       * });
       *
       * curried(1)(2)(3);
       * // => 6
       *
       * curried(1, 2)(3);
       * // => 6
       *
       * curried(1, 2, 3);
       * // => 6
       */
      function curry(func, arity) {
        arity = typeof arity == 'number' ? arity : (+arity || func.length);
        return createWrapper(func, 4, null, null, null, arity);
      }
  
      /**
       * Creates a function that will delay the execution of `func` until after
       * `wait` milliseconds have elapsed since the last time it was invoked.
       * Provide an options object to indicate that `func` should be invoked on
       * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
       * to the debounced function will return the result of the last `func` call.
       *
       * Note: If `leading` and `trailing` options are `true` `func` will be called
       * on the trailing edge of the timeout only if the the debounced function is
       * invoked more than once during the `wait` timeout.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to debounce.
       * @param {number} wait The number of milliseconds to delay.
       * @param {Object} [options] The options object.
       * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
       * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
       * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
       * @returns {Function} Returns the new debounced function.
       * @example
       *
       * // avoid costly calculations while the window size is in flux
       * var lazyLayout = _.debounce(calculateLayout, 150);
       * jQuery(window).on('resize', lazyLayout);
       *
       * // execute `sendMail` when the click event is fired, debouncing subsequent calls
       * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
       *   'leading': true,
       *   'trailing': false
       * });
       *
       * // ensure `batchLog` is executed once after 1 second of debounced calls
       * var source = new EventSource('/stream');
       * source.addEventListener('message', _.debounce(batchLog, 250, {
       *   'maxWait': 1000
       * }, false);
       */
      function debounce(func, wait, options) {
        var args,
            maxTimeoutId,
            result,
            stamp,
            thisArg,
            timeoutId,
            trailingCall,
            lastCalled = 0,
            maxWait = false,
            trailing = true;
  
        if (!isFunction(func)) {
          throw new TypeError;
        }
        wait = nativeMax(0, wait) || 0;
        if (options === true) {
          var leading = true;
          trailing = false;
        } else if (isObject(options)) {
          leading = options.leading;
          maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
          trailing = 'trailing' in options ? options.trailing : trailing;
        }
        var delayed = function() {
          var remaining = wait - (now() - stamp);
          if (remaining <= 0) {
            if (maxTimeoutId) {
              clearTimeout(maxTimeoutId);
            }
            var isCalled = trailingCall;
            maxTimeoutId = timeoutId = trailingCall = undefined;
            if (isCalled) {
              lastCalled = now();
              result = func.apply(thisArg, args);
              if (!timeoutId && !maxTimeoutId) {
                args = thisArg = null;
              }
            }
          } else {
            timeoutId = setTimeout(delayed, remaining);
          }
        };
  
        var maxDelayed = function() {
          if (timeoutId) {
            clearTimeout(timeoutId);
          }
          maxTimeoutId = timeoutId = trailingCall = undefined;
          if (trailing || (maxWait !== wait)) {
            lastCalled = now();
            result = func.apply(thisArg, args);
            if (!timeoutId && !maxTimeoutId) {
              args = thisArg = null;
            }
          }
        };
  
        return function() {
          args = arguments;
          stamp = now();
          thisArg = this;
          trailingCall = trailing && (timeoutId || !leading);
  
          if (maxWait === false) {
            var leadingCall = leading && !timeoutId;
          } else {
            if (!maxTimeoutId && !leading) {
              lastCalled = stamp;
            }
            var remaining = maxWait - (stamp - lastCalled),
                isCalled = remaining <= 0;
  
            if (isCalled) {
              if (maxTimeoutId) {
                maxTimeoutId = clearTimeout(maxTimeoutId);
              }
              lastCalled = stamp;
              result = func.apply(thisArg, args);
            }
            else if (!maxTimeoutId) {
              maxTimeoutId = setTimeout(maxDelayed, remaining);
            }
          }
          if (isCalled && timeoutId) {
            timeoutId = clearTimeout(timeoutId);
          }
          else if (!timeoutId && wait !== maxWait) {
            timeoutId = setTimeout(delayed, wait);
          }
          if (leadingCall) {
            isCalled = true;
            result = func.apply(thisArg, args);
          }
          if (isCalled && !timeoutId && !maxTimeoutId) {
            args = thisArg = null;
          }
          return result;
        };
      }
  
      /**
       * Defers executing the `func` function until the current call stack has cleared.
       * Additional arguments will be provided to `func` when it is invoked.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to defer.
       * @param {...*} [arg] Arguments to invoke the function with.
       * @returns {number} Returns the timer id.
       * @example
       *
       * _.defer(function(text) { console.log(text); }, 'deferred');
       * // logs 'deferred' after one or more milliseconds
       */
      function defer(func) {
        if (!isFunction(func)) {
          throw new TypeError;
        }
        var args = slice(arguments, 1);
        return setTimeout(function() { func.apply(undefined, args); }, 1);
      }
  
      /**
       * Executes the `func` function after `wait` milliseconds. Additional arguments
       * will be provided to `func` when it is invoked.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to delay.
       * @param {number} wait The number of milliseconds to delay execution.
       * @param {...*} [arg] Arguments to invoke the function with.
       * @returns {number} Returns the timer id.
       * @example
       *
       * _.delay(function(text) { console.log(text); }, 1000, 'later');
       * // => logs 'later' after one second
       */
      function delay(func, wait) {
        if (!isFunction(func)) {
          throw new TypeError;
        }
        var args = slice(arguments, 2);
        return setTimeout(function() { func.apply(undefined, args); }, wait);
      }
  
      /**
       * Creates a function that memoizes the result of `func`. If `resolver` is
       * provided it will be used to determine the cache key for storing the result
       * based on the arguments provided to the memoized function. By default, the
       * first argument provided to the memoized function is used as the cache key.
       * The `func` is executed with the `this` binding of the memoized function.
       * The result cache is exposed as the `cache` property on the memoized function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to have its output memoized.
       * @param {Function} [resolver] A function used to resolve the cache key.
       * @returns {Function} Returns the new memoizing function.
       * @example
       *
       * var fibonacci = _.memoize(function(n) {
       *   return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
       * });
       *
       * fibonacci(9)
       * // => 34
       *
       * var data = {
       *   'fred': { 'name': 'fred', 'age': 40 },
       *   'pebbles': { 'name': 'pebbles', 'age': 1 }
       * };
       *
       * // modifying the result cache
       * var get = _.memoize(function(name) { return data[name]; }, _.identity);
       * get('pebbles');
       * // => { 'name': 'pebbles', 'age': 1 }
       *
       * get.cache.pebbles.name = 'penelope';
       * get('pebbles');
       * // => { 'name': 'penelope', 'age': 1 }
       */
      function memoize(func, resolver) {
        if (!isFunction(func)) {
          throw new TypeError;
        }
        var memoized = function() {
          var cache = memoized.cache,
              key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
  
          return hasOwnProperty.call(cache, key)
            ? cache[key]
            : (cache[key] = func.apply(this, arguments));
        }
        memoized.cache = {};
        return memoized;
      }
  
      /**
       * Creates a function that is restricted to execute `func` once. Repeat calls to
       * the function will return the value of the first call. The `func` is executed
       * with the `this` binding of the created function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to restrict.
       * @returns {Function} Returns the new restricted function.
       * @example
       *
       * var initialize = _.once(createApplication);
       * initialize();
       * initialize();
       * // `initialize` executes `createApplication` once
       */
      function once(func) {
        var ran,
            result;
  
        if (!isFunction(func)) {
          throw new TypeError;
        }
        return function() {
          if (ran) {
            return result;
          }
          ran = true;
          result = func.apply(this, arguments);
  
          // clear the `func` variable so the function may be garbage collected
          func = null;
          return result;
        };
      }
  
      /**
       * Creates a function that, when called, invokes `func` with any additional
       * `partial` arguments prepended to those provided to the new function. This
       * method is similar to `_.bind` except it does **not** alter the `this` binding.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to partially apply arguments to.
       * @param {...*} [arg] Arguments to be partially applied.
       * @returns {Function} Returns the new partially applied function.
       * @example
       *
       * var greet = function(greeting, name) { return greeting + ' ' + name; };
       * var hi = _.partial(greet, 'hi');
       * hi('fred');
       * // => 'hi fred'
       */
      function partial(func) {
        return createWrapper(func, 16, slice(arguments, 1));
      }
  
      /**
       * This method is like `_.partial` except that `partial` arguments are
       * appended to those provided to the new function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to partially apply arguments to.
       * @param {...*} [arg] Arguments to be partially applied.
       * @returns {Function} Returns the new partially applied function.
       * @example
       *
       * var defaultsDeep = _.partialRight(_.merge, _.defaults);
       *
       * var options = {
       *   'variable': 'data',
       *   'imports': { 'jq': $ }
       * };
       *
       * defaultsDeep(options, _.templateSettings);
       *
       * options.variable
       * // => 'data'
       *
       * options.imports
       * // => { '_': _, 'jq': $ }
       */
      function partialRight(func) {
        return createWrapper(func, 32, null, slice(arguments, 1));
      }
  
      /**
       * Creates a function that, when executed, will only call the `func` function
       * at most once per every `wait` milliseconds. Provide an options object to
       * indicate that `func` should be invoked on the leading and/or trailing edge
       * of the `wait` timeout. Subsequent calls to the throttled function will
       * return the result of the last `func` call.
       *
       * Note: If `leading` and `trailing` options are `true` `func` will be called
       * on the trailing edge of the timeout only if the the throttled function is
       * invoked more than once during the `wait` timeout.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {Function} func The function to throttle.
       * @param {number} wait The number of milliseconds to throttle executions to.
       * @param {Object} [options] The options object.
       * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
       * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
       * @returns {Function} Returns the new throttled function.
       * @example
       *
       * // avoid excessively updating the position while scrolling
       * var throttled = _.throttle(updatePosition, 100);
       * jQuery(window).on('scroll', throttled);
       *
       * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
       * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
       *   'trailing': false
       * }));
       */
      function throttle(func, wait, options) {
        var leading = true,
            trailing = true;
  
        if (!isFunction(func)) {
          throw new TypeError;
        }
        if (options === false) {
          leading = false;
        } else if (isObject(options)) {
          leading = 'leading' in options ? options.leading : leading;
          trailing = 'trailing' in options ? options.trailing : trailing;
        }
        debounceOptions.leading = leading;
        debounceOptions.maxWait = wait;
        debounceOptions.trailing = trailing;
  
        return debounce(func, wait, debounceOptions);
      }
  
      /**
       * Creates a function that provides `value` to the wrapper function as its
       * first argument. Additional arguments provided to the function are appended
       * to those provided to the wrapper function. The wrapper is executed with
       * the `this` binding of the created function.
       *
       * @static
       * @memberOf _
       * @category Functions
       * @param {*} value The value to wrap.
       * @param {Function} wrapper The wrapper function.
       * @returns {Function} Returns the new function.
       * @example
       *
       * var p = _.wrap(_.escape, function(func, text) {
       *   return '<p>' + func(text) + '</p>';
       * });
       *
       * p('Fred, Wilma, & Pebbles');
       * // => '<p>Fred, Wilma, &amp; Pebbles</p>'
       */
      function wrap(value, wrapper) {
        return createWrapper(wrapper, 16, [value]);
      }
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates a function that returns `value`.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {*} value The value to return from the new function.
       * @returns {Function} Returns the new function.
       * @example
       *
       * var object = { 'name': 'fred' };
       * var getter = _.constant(object);
       * getter() === object;
       * // => true
       */
      function constant(value) {
        return function() {
          return value;
        };
      }
  
      /**
       * Produces a callback bound to an optional `thisArg`. If `func` is a property
       * name the created callback will return the property value for a given element.
       * If `func` is an object the created callback will return `true` for elements
       * that contain the equivalent object properties, otherwise it will return `false`.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {*} [func=identity] The value to convert to a callback.
       * @param {*} [thisArg] The `this` binding of the created callback.
       * @param {number} [argCount] The number of arguments the callback accepts.
       * @returns {Function} Returns a callback function.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * // wrap to create custom callback shorthands
       * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
       *   var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
       *   return !match ? func(callback, thisArg) : function(object) {
       *     return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
       *   };
       * });
       *
       * _.filter(characters, 'age__gt38');
       * // => [{ 'name': 'fred', 'age': 40 }]
       */
      function createCallback(func, thisArg, argCount) {
        var type = typeof func;
        if (func == null || type == 'function') {
          return baseCreateCallback(func, thisArg, argCount);
        }
        // handle "_.pluck" style callback shorthands
        if (type != 'object') {
          return property(func);
        }
        var props = keys(func),
            key = props[0],
            a = func[key];
  
        // handle "_.where" style callback shorthands
        if (props.length == 1 && a === a && !isObject(a)) {
          // fast path the common case of providing an object with a single
          // property containing a primitive value
          return function(object) {
            var b = object[key];
            return a === b && (a !== 0 || (1 / a == 1 / b));
          };
        }
        return function(object) {
          var length = props.length,
              result = false;
  
          while (length--) {
            if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
              break;
            }
          }
          return result;
        };
      }
  
      /**
       * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
       * corresponding HTML entities.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} string The string to escape.
       * @returns {string} Returns the escaped string.
       * @example
       *
       * _.escape('Fred, Wilma, & Pebbles');
       * // => 'Fred, Wilma, &amp; Pebbles'
       */
      function escape(string) {
        return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
      }
  
      /**
       * This method returns the first argument provided to it.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {*} value Any value.
       * @returns {*} Returns `value`.
       * @example
       *
       * var object = { 'name': 'fred' };
       * _.identity(object) === object;
       * // => true
       */
      function identity(value) {
        return value;
      }
  
      /**
       * Adds function properties of a source object to the destination object.
       * If `object` is a function methods will be added to its prototype as well.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {Function|Object} [object=lodash] object The destination object.
       * @param {Object} source The object of functions to add.
       * @param {Object} [options] The options object.
       * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
       * @example
       *
       * function capitalize(string) {
       *   return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
       * }
       *
       * _.mixin({ 'capitalize': capitalize });
       * _.capitalize('fred');
       * // => 'Fred'
       *
       * _('fred').capitalize().value();
       * // => 'Fred'
       *
       * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
       * _('fred').capitalize();
       * // => 'Fred'
       */
      function mixin(object, source, options) {
        var chain = true,
            methodNames = source && functions(source);
  
        if (!source || (!options && !methodNames.length)) {
          if (options == null) {
            options = source;
          }
          ctor = lodashWrapper;
          source = object;
          object = lodash;
          methodNames = functions(source);
        }
        if (options === false) {
          chain = false;
        } else if (isObject(options) && 'chain' in options) {
          chain = options.chain;
        }
        var ctor = object,
            isFunc = isFunction(ctor);
  
        forEach(methodNames, function(methodName) {
          var func = object[methodName] = source[methodName];
          if (isFunc) {
            ctor.prototype[methodName] = function() {
              var chainAll = this.__chain__,
                  value = this.__wrapped__,
                  args = [value];
  
              push.apply(args, arguments);
              var result = func.apply(object, args);
              if (chain || chainAll) {
                if (value === result && isObject(result)) {
                  return this;
                }
                result = new ctor(result);
                result.__chain__ = chainAll;
              }
              return result;
            };
          }
        });
      }
  
      /**
       * Reverts the '_' variable to its previous value and returns a reference to
       * the `lodash` function.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @returns {Function} Returns the `lodash` function.
       * @example
       *
       * var lodash = _.noConflict();
       */
      function noConflict() {
        context._ = oldDash;
        return this;
      }
  
      /**
       * A no-operation function.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @example
       *
       * var object = { 'name': 'fred' };
       * _.noop(object) === undefined;
       * // => true
       */
      function noop() {
        // no operation performed
      }
  
      /**
       * Gets the number of milliseconds that have elapsed since the Unix epoch
       * (1 January 1970 00:00:00 UTC).
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @example
       *
       * var stamp = _.now();
       * _.defer(function() { console.log(_.now() - stamp); });
       * // => logs the number of milliseconds it took for the deferred function to be called
       */
      var now = isNative(now = Date.now) && now || function() {
        return new Date().getTime();
      };
  
      /**
       * Converts the given value into an integer of the specified radix.
       * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
       * `value` is a hexadecimal, in which case a `radix` of `16` is used.
       *
       * Note: This method avoids differences in native ES3 and ES5 `parseInt`
       * implementations. See http://es5.github.io/#E.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} value The value to parse.
       * @param {number} [radix] The radix used to interpret the value to parse.
       * @returns {number} Returns the new integer value.
       * @example
       *
       * _.parseInt('08');
       * // => 8
       */
      var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
        // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
        return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
      };
  
      /**
       * Creates a "_.pluck" style function, which returns the `key` value of a
       * given object.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} key The name of the property to retrieve.
       * @returns {Function} Returns the new function.
       * @example
       *
       * var characters = [
       *   { 'name': 'fred',   'age': 40 },
       *   { 'name': 'barney', 'age': 36 }
       * ];
       *
       * var getName = _.property('name');
       *
       * _.map(characters, getName);
       * // => ['barney', 'fred']
       *
       * _.sortBy(characters, getName);
       * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred',   'age': 40 }]
       */
      function property(key) {
        return function(object) {
          return object[key];
        };
      }
  
      /**
       * Produces a random number between `min` and `max` (inclusive). If only one
       * argument is provided a number between `0` and the given number will be
       * returned. If `floating` is truey or either `min` or `max` are floats a
       * floating-point number will be returned instead of an integer.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {number} [min=0] The minimum possible value.
       * @param {number} [max=1] The maximum possible value.
       * @param {boolean} [floating=false] Specify returning a floating-point number.
       * @returns {number} Returns a random number.
       * @example
       *
       * _.random(0, 5);
       * // => an integer between 0 and 5
       *
       * _.random(5);
       * // => also an integer between 0 and 5
       *
       * _.random(5, true);
       * // => a floating-point number between 0 and 5
       *
       * _.random(1.2, 5.2);
       * // => a floating-point number between 1.2 and 5.2
       */
      function random(min, max, floating) {
        var noMin = min == null,
            noMax = max == null;
  
        if (floating == null) {
          if (typeof min == 'boolean' && noMax) {
            floating = min;
            min = 1;
          }
          else if (!noMax && typeof max == 'boolean') {
            floating = max;
            noMax = true;
          }
        }
        if (noMin && noMax) {
          max = 1;
        }
        min = +min || 0;
        if (noMax) {
          max = min;
          min = 0;
        } else {
          max = +max || 0;
        }
        if (floating || min % 1 || max % 1) {
          var rand = nativeRandom();
          return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
        }
        return baseRandom(min, max);
      }
  
      /**
       * Resolves the value of property `key` on `object`. If `key` is a function
       * it will be invoked with the `this` binding of `object` and its result returned,
       * else the property value is returned. If `object` is falsey then `undefined`
       * is returned.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {Object} object The object to inspect.
       * @param {string} key The name of the property to resolve.
       * @returns {*} Returns the resolved value.
       * @example
       *
       * var object = {
       *   'cheese': 'crumpets',
       *   'stuff': function() {
       *     return 'nonsense';
       *   }
       * };
       *
       * _.result(object, 'cheese');
       * // => 'crumpets'
       *
       * _.result(object, 'stuff');
       * // => 'nonsense'
       */
      function result(object, key) {
        if (object) {
          var value = object[key];
          return isFunction(value) ? object[key]() : value;
        }
      }
  
      /**
       * A micro-templating method that handles arbitrary delimiters, preserves
       * whitespace, and correctly escapes quotes within interpolated code.
       *
       * Note: In the development build, `_.template` utilizes sourceURLs for easier
       * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
       *
       * For more information on precompiling templates see:
       * http://lodash.com/custom-builds
       *
       * For more information on Chrome extension sandboxes see:
       * http://developer.chrome.com/stable/extensions/sandboxingEval.html
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} text The template text.
       * @param {Object} data The data object used to populate the text.
       * @param {Object} [options] The options object.
       * @param {RegExp} [options.escape] The "escape" delimiter.
       * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
       * @param {Object} [options.imports] An object to import into the template as local variables.
       * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
       * @param {string} [sourceURL] The sourceURL of the template's compiled source.
       * @param {string} [variable] The data object variable name.
       * @returns {Function|string} Returns a compiled function when no `data` object
       *  is given, else it returns the interpolated text.
       * @example
       *
       * // using the "interpolate" delimiter to create a compiled template
       * var compiled = _.template('hello <%= name %>');
       * compiled({ 'name': 'fred' });
       * // => 'hello fred'
       *
       * // using the "escape" delimiter to escape HTML in data property values
       * _.template('<b><%- value %></b>', { 'value': '<script>' });
       * // => '<b>&lt;script&gt;</b>'
       *
       * // using the "evaluate" delimiter to generate HTML
       * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
       * _.template(list, { 'people': ['fred', 'barney'] });
       * // => '<li>fred</li><li>barney</li>'
       *
       * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
       * _.template('hello ${ name }', { 'name': 'pebbles' });
       * // => 'hello pebbles'
       *
       * // using the internal `print` function in "evaluate" delimiters
       * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
       * // => 'hello barney!'
       *
       * // using a custom template delimiters
       * _.templateSettings = {
       *   'interpolate': /{{([\s\S]+?)}}/g
       * };
       *
       * _.template('hello {{ name }}!', { 'name': 'mustache' });
       * // => 'hello mustache!'
       *
       * // using the `imports` option to import jQuery
       * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
       * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
       * // => '<li>fred</li><li>barney</li>'
       *
       * // using the `sourceURL` option to specify a custom sourceURL for the template
       * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
       * compiled(data);
       * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
       *
       * // using the `variable` option to ensure a with-statement isn't used in the compiled template
       * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
       * compiled.source;
       * // => function(data) {
       *   var __t, __p = '', __e = _.escape;
       *   __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
       *   return __p;
       * }
       *
       * // using the `source` property to inline compiled templates for meaningful
       * // line numbers in error messages and a stack trace
       * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
       *   var JST = {\
       *     "main": ' + _.template(mainText).source + '\
       *   };\
       * ');
       */
      function template(text, data, options) {
        // based on John Resig's `tmpl` implementation
        // http://ejohn.org/blog/javascript-micro-templating/
        // and Laura Doktorova's doT.js
        // https://github.com/olado/doT
        var settings = lodash.templateSettings;
        text = String(text || '');
  
        // avoid missing dependencies when `iteratorTemplate` is not defined
        options = defaults({}, options, settings);
  
        var imports = defaults({}, options.imports, settings.imports),
            importsKeys = keys(imports),
            importsValues = values(imports);
  
        var isEvaluating,
            index = 0,
            interpolate = options.interpolate || reNoMatch,
            source = "__p += '";
  
        // compile the regexp to match each delimiter
        var reDelimiters = RegExp(
          (options.escape || reNoMatch).source + '|' +
          interpolate.source + '|' +
          (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
          (options.evaluate || reNoMatch).source + '|$'
        , 'g');
  
        text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
          interpolateValue || (interpolateValue = esTemplateValue);
  
          // escape characters that cannot be included in string literals
          source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
  
          // replace delimiters with snippets
          if (escapeValue) {
            source += "' +\n__e(" + escapeValue + ") +\n'";
          }
          if (evaluateValue) {
            isEvaluating = true;
            source += "';\n" + evaluateValue + ";\n__p += '";
          }
          if (interpolateValue) {
            source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
          }
          index = offset + match.length;
  
          // the JS engine embedded in Adobe products requires returning the `match`
          // string in order to produce the correct `offset` value
          return match;
        });
  
        source += "';\n";
  
        // if `variable` is not specified, wrap a with-statement around the generated
        // code to add the data object to the top of the scope chain
        var variable = options.variable,
            hasVariable = variable;
  
        if (!hasVariable) {
          variable = 'obj';
          source = 'with (' + variable + ') {\n' + source + '\n}\n';
        }
        // cleanup code by stripping empty strings
        source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
          .replace(reEmptyStringMiddle, '$1')
          .replace(reEmptyStringTrailing, '$1;');
  
        // frame code as the function body
        source = 'function(' + variable + ') {\n' +
          (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
          "var __t, __p = '', __e = _.escape" +
          (isEvaluating
            ? ', __j = Array.prototype.join;\n' +
              "function print() { __p += __j.call(arguments, '') }\n"
            : ';\n'
          ) +
          source +
          'return __p\n}';
  
        // Use a sourceURL for easier debugging.
        // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
        var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
  
        try {
          var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
        } catch(e) {
          e.source = source;
          throw e;
        }
        if (data) {
          return result(data);
        }
        // provide the compiled function's source by its `toString` method, in
        // supported environments, or the `source` property as a convenience for
        // inlining compiled templates during the build process
        result.source = source;
        return result;
      }
  
      /**
       * Executes the callback `n` times, returning an array of the results
       * of each callback execution. The callback is bound to `thisArg` and invoked
       * with one argument; (index).
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {number} n The number of times to execute the callback.
       * @param {Function} callback The function called per iteration.
       * @param {*} [thisArg] The `this` binding of `callback`.
       * @returns {Array} Returns an array of the results of each `callback` execution.
       * @example
       *
       * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
       * // => [3, 6, 4]
       *
       * _.times(3, function(n) { mage.castSpell(n); });
       * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
       *
       * _.times(3, function(n) { this.cast(n); }, mage);
       * // => also calls `mage.castSpell(n)` three times
       */
      function times(n, callback, thisArg) {
        n = (n = +n) > -1 ? n : 0;
        var index = -1,
            result = Array(n);
  
        callback = baseCreateCallback(callback, thisArg, 1);
        while (++index < n) {
          result[index] = callback(index);
        }
        return result;
      }
  
      /**
       * The inverse of `_.escape` this method converts the HTML entities
       * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to their
       * corresponding characters.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} string The string to unescape.
       * @returns {string} Returns the unescaped string.
       * @example
       *
       * _.unescape('Fred, Barney &amp; Pebbles');
       * // => 'Fred, Barney & Pebbles'
       */
      function unescape(string) {
        return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
      }
  
      /**
       * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
       *
       * @static
       * @memberOf _
       * @category Utilities
       * @param {string} [prefix] The value to prefix the ID with.
       * @returns {string} Returns the unique ID.
       * @example
       *
       * _.uniqueId('contact_');
       * // => 'contact_104'
       *
       * _.uniqueId();
       * // => '105'
       */
      function uniqueId(prefix) {
        var id = ++idCounter;
        return String(prefix == null ? '' : prefix) + id;
      }
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * Creates a `lodash` object that wraps the given value with explicit
       * method chaining enabled.
       *
       * @static
       * @memberOf _
       * @category Chaining
       * @param {*} value The value to wrap.
       * @returns {Object} Returns the wrapper object.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney',  'age': 36 },
       *   { 'name': 'fred',    'age': 40 },
       *   { 'name': 'pebbles', 'age': 1 }
       * ];
       *
       * var youngest = _.chain(characters)
       *     .sortBy('age')
       *     .map(function(chr) { return chr.name + ' is ' + chr.age; })
       *     .first()
       *     .value();
       * // => 'pebbles is 1'
       */
      function chain(value) {
        value = new lodashWrapper(value);
        value.__chain__ = true;
        return value;
      }
  
      /**
       * Invokes `interceptor` with the `value` as the first argument and then
       * returns `value`. The purpose of this method is to "tap into" a method
       * chain in order to perform operations on intermediate results within
       * the chain.
       *
       * @static
       * @memberOf _
       * @category Chaining
       * @param {*} value The value to provide to `interceptor`.
       * @param {Function} interceptor The function to invoke.
       * @returns {*} Returns `value`.
       * @example
       *
       * _([1, 2, 3, 4])
       *  .tap(function(array) { array.pop(); })
       *  .reverse()
       *  .value();
       * // => [3, 2, 1]
       */
      function tap(value, interceptor) {
        interceptor(value);
        return value;
      }
  
      /**
       * Enables explicit method chaining on the wrapper object.
       *
       * @name chain
       * @memberOf _
       * @category Chaining
       * @returns {*} Returns the wrapper object.
       * @example
       *
       * var characters = [
       *   { 'name': 'barney', 'age': 36 },
       *   { 'name': 'fred',   'age': 40 }
       * ];
       *
       * // without explicit chaining
       * _(characters).first();
       * // => { 'name': 'barney', 'age': 36 }
       *
       * // with explicit chaining
       * _(characters).chain()
       *   .first()
       *   .pick('age')
       *   .value();
       * // => { 'age': 36 }
       */
      function wrapperChain() {
        this.__chain__ = true;
        return this;
      }
  
      /**
       * Produces the `toString` result of the wrapped value.
       *
       * @name toString
       * @memberOf _
       * @category Chaining
       * @returns {string} Returns the string result.
       * @example
       *
       * _([1, 2, 3]).toString();
       * // => '1,2,3'
       */
      function wrapperToString() {
        return String(this.__wrapped__);
      }
  
      /**
       * Extracts the wrapped value.
       *
       * @name valueOf
       * @memberOf _
       * @alias value
       * @category Chaining
       * @returns {*} Returns the wrapped value.
       * @example
       *
       * _([1, 2, 3]).valueOf();
       * // => [1, 2, 3]
       */
      function wrapperValueOf() {
        return this.__wrapped__;
      }
  
      /*--------------------------------------------------------------------------*/
  
      // add functions that return wrapped values when chaining
      lodash.after = after;
      lodash.assign = assign;
      lodash.at = at;
      lodash.bind = bind;
      lodash.bindAll = bindAll;
      lodash.bindKey = bindKey;
      lodash.chain = chain;
      lodash.compact = compact;
      lodash.compose = compose;
      lodash.constant = constant;
      lodash.countBy = countBy;
      lodash.create = create;
      lodash.createCallback = createCallback;
      lodash.curry = curry;
      lodash.debounce = debounce;
      lodash.defaults = defaults;
      lodash.defer = defer;
      lodash.delay = delay;
      lodash.difference = difference;
      lodash.filter = filter;
      lodash.flatten = flatten;
      lodash.forEach = forEach;
      lodash.forEachRight = forEachRight;
      lodash.forIn = forIn;
      lodash.forInRight = forInRight;
      lodash.forOwn = forOwn;
      lodash.forOwnRight = forOwnRight;
      lodash.functions = functions;
      lodash.groupBy = groupBy;
      lodash.indexBy = indexBy;
      lodash.initial = initial;
      lodash.intersection = intersection;
      lodash.invert = invert;
      lodash.invoke = invoke;
      lodash.keys = keys;
      lodash.map = map;
      lodash.mapValues = mapValues;
      lodash.max = max;
      lodash.memoize = memoize;
      lodash.merge = merge;
      lodash.min = min;
      lodash.omit = omit;
      lodash.once = once;
      lodash.pairs = pairs;
      lodash.partial = partial;
      lodash.partialRight = partialRight;
      lodash.pick = pick;
      lodash.pluck = pluck;
      lodash.property = property;
      lodash.pull = pull;
      lodash.range = range;
      lodash.reject = reject;
      lodash.remove = remove;
      lodash.rest = rest;
      lodash.shuffle = shuffle;
      lodash.sortBy = sortBy;
      lodash.tap = tap;
      lodash.throttle = throttle;
      lodash.times = times;
      lodash.toArray = toArray;
      lodash.transform = transform;
      lodash.union = union;
      lodash.uniq = uniq;
      lodash.values = values;
      lodash.where = where;
      lodash.without = without;
      lodash.wrap = wrap;
      lodash.xor = xor;
      lodash.zip = zip;
      lodash.zipObject = zipObject;
  
      // add aliases
      lodash.collect = map;
      lodash.drop = rest;
      lodash.each = forEach;
      lodash.eachRight = forEachRight;
      lodash.extend = assign;
      lodash.methods = functions;
      lodash.object = zipObject;
      lodash.select = filter;
      lodash.tail = rest;
      lodash.unique = uniq;
      lodash.unzip = zip;
  
      // add functions to `lodash.prototype`
      mixin(lodash);
  
      /*--------------------------------------------------------------------------*/
  
      // add functions that return unwrapped values when chaining
      lodash.clone = clone;
      lodash.cloneDeep = cloneDeep;
      lodash.contains = contains;
      lodash.escape = escape;
      lodash.every = every;
      lodash.find = find;
      lodash.findIndex = findIndex;
      lodash.findKey = findKey;
      lodash.findLast = findLast;
      lodash.findLastIndex = findLastIndex;
      lodash.findLastKey = findLastKey;
      lodash.has = has;
      lodash.identity = identity;
      lodash.indexOf = indexOf;
      lodash.isArguments = isArguments;
      lodash.isArray = isArray;
      lodash.isBoolean = isBoolean;
      lodash.isDate = isDate;
      lodash.isElement = isElement;
      lodash.isEmpty = isEmpty;
      lodash.isEqual = isEqual;
      lodash.isFinite = isFinite;
      lodash.isFunction = isFunction;
      lodash.isNaN = isNaN;
      lodash.isNull = isNull;
      lodash.isNumber = isNumber;
      lodash.isObject = isObject;
      lodash.isPlainObject = isPlainObject;
      lodash.isRegExp = isRegExp;
      lodash.isString = isString;
      lodash.isUndefined = isUndefined;
      lodash.lastIndexOf = lastIndexOf;
      lodash.mixin = mixin;
      lodash.noConflict = noConflict;
      lodash.noop = noop;
      lodash.now = now;
      lodash.parseInt = parseInt;
      lodash.random = random;
      lodash.reduce = reduce;
      lodash.reduceRight = reduceRight;
      lodash.result = result;
      lodash.runInContext = runInContext;
      lodash.size = size;
      lodash.some = some;
      lodash.sortedIndex = sortedIndex;
      lodash.template = template;
      lodash.unescape = unescape;
      lodash.uniqueId = uniqueId;
  
      // add aliases
      lodash.all = every;
      lodash.any = some;
      lodash.detect = find;
      lodash.findWhere = find;
      lodash.foldl = reduce;
      lodash.foldr = reduceRight;
      lodash.include = contains;
      lodash.inject = reduce;
  
      mixin(function() {
        var source = {}
        forOwn(lodash, function(func, methodName) {
          if (!lodash.prototype[methodName]) {
            source[methodName] = func;
          }
        });
        return source;
      }(), false);
  
      /*--------------------------------------------------------------------------*/
  
      // add functions capable of returning wrapped and unwrapped values when chaining
      lodash.first = first;
      lodash.last = last;
      lodash.sample = sample;
  
      // add aliases
      lodash.take = first;
      lodash.head = first;
  
      forOwn(lodash, function(func, methodName) {
        var callbackable = methodName !== 'sample';
        if (!lodash.prototype[methodName]) {
          lodash.prototype[methodName]= function(n, guard) {
            var chainAll = this.__chain__,
                result = func(this.__wrapped__, n, guard);
  
            return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
              ? result
              : new lodashWrapper(result, chainAll);
          };
        }
      });
  
      /*--------------------------------------------------------------------------*/
  
      /**
       * The semantic version number.
       *
       * @static
       * @memberOf _
       * @type string
       */
      lodash.VERSION = '2.4.1';
  
      // add "Chaining" functions to the wrapper
      lodash.prototype.chain = wrapperChain;
      lodash.prototype.toString = wrapperToString;
      lodash.prototype.value = wrapperValueOf;
      lodash.prototype.valueOf = wrapperValueOf;
  
      // add `Array` functions that return unwrapped values
      forEach(['join', 'pop', 'shift'], function(methodName) {
        var func = arrayRef[methodName];
        lodash.prototype[methodName] = function() {
          var chainAll = this.__chain__,
              result = func.apply(this.__wrapped__, arguments);
  
          return chainAll
            ? new lodashWrapper(result, chainAll)
            : result;
        };
      });
  
      // add `Array` functions that return the existing wrapped value
      forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
        var func = arrayRef[methodName];
        lodash.prototype[methodName] = function() {
          func.apply(this.__wrapped__, arguments);
          return this;
        };
      });
  
      // add `Array` functions that return new wrapped values
      forEach(['concat', 'slice', 'splice'], function(methodName) {
        var func = arrayRef[methodName];
        lodash.prototype[methodName] = function() {
          return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
        };
      });
  
      return lodash;
    }
  
    /*--------------------------------------------------------------------------*/
  
    // expose Lo-Dash
    var _ = runInContext();
  
    // some AMD build optimizers like r.js check for condition patterns like the following:
    if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
      // Expose Lo-Dash to the global object even when an AMD loader is present in
      // case Lo-Dash is loaded with a RequireJS shim config.
      // See http://requirejs.org/docs/api.html#config-shim
      root._ = _;
  
      // define as an anonymous module so, through path mapping, it can be
      // referenced as the "underscore" module
      define(function() {
        return _;
      });
    }
    // check for `exports` after `define` in case a build optimizer adds an `exports` object
    else if (freeExports && freeModule) {
      // in Node.js or RingoJS
      if (moduleExports) {
        (freeModule.exports = _)._ = _;
      }
      // in Narwhal or Rhino -require
      else {
        freeExports._ = _;
      }
    }
    else {
      // in a browser or Rhino
      root._ = _;
    }
  }.call(this));
  
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  },{}]},{},[1]);
  